import { DataSource } from "typeorm";
import { Article } from "./modules/articles/entities/article.entity";
import { User } from "./modules/users/entities/user.entity";
import * as marked from 'marked';

// 配置 marked
marked.setOptions({
  breaks: true,
  gfm: true,
});

export async function seedArticles(AppDataSource: DataSource) {
  console.log("📝 开始导入文章数据...");

  const articleRepository = AppDataSource.getRepository(Article);
  const userRepository = AppDataSource.getRepository(User);

  // 获取管理员用户
  const admin = await userRepository.findOne({
    where: { email: "1123654054@qq.com" },
  });
  if (!admin) {
    console.log("❌ 未找到管理员账号");
    return;
  }

  // 清空现有文章
  await articleRepository.clear();
  console.log("✅ 清空现有文章数据");
  const articlesData = [
    // 文章1：个人经验分享
    {
      title: "从Vue2迁移到Vue3，我踩过的那些坑",
      slug: "vue2-to-vue3-migration-pitfalls",
      excerpt:
        "公司项目最近从Vue2升级到Vue3，折腾了两周，记录一下遇到的问题和解决方案，希望能帮到正在迁移的朋友。",
      content: `
去年年底老板说要把项目升级到Vue3，我当时想着Vue3都出来这么久了，应该挺成熟了吧，结果真开始迁移才发现，坑是真的多。

## 为什么要升级

说实话一开始我是拒绝的，Vue2用得好好的，为啥要折腾？但架不住老板天天念叨：
- Composition API真香，代码复用更方便
- 性能提升，打包体积更小
- TypeScript支持更好
- Vue2官方已经不维护了

最后一条是关键，不升不行了。

## 遇到的主要问题

### 1. Vue Router的变化

这个是第一个让我头疼的。以前Vue2里直接 \`new VueRouter()\`，现在要用 \`createRouter\`。而且history模式的写法也变了：

\`\`\`javascript
// Vue2
mode: 'history'

// Vue3  
history: createWebHistory()
\`\`\`

项目里有几十个页面，路由守卫也得全改。我花了整整一天时间在这上面。

### 2. Vuex到Pinia的迁移

老实说Vuex我用得挺顺手的，但既然都升级了，索性换成Pinia吧。Pinia确实简洁，不用mutations了，直接改state就行。

但问题来了，项目里有100多个Vuex的模块，全改工作量巨大。最后我采用了渐进式迁移，先保留Vuex，新功能用Pinia，慢慢替换。

### 3. 生命周期钩子的变化

\`beforeDestroy\` 改成 \`beforeUnmount\`
\`destroyed\` 改成 \`unmounted\`

这个还好，全局搜索替换就行了。但要小心Composition API里的写法，是 \`onBeforeUnmount\` 和 \`onUnmounted\`。

### 4. v-model的变化

这个变化挺大的。Vue3里自定义组件的v-model默认prop变成了\`modelValue\`，事件变成了\`update:modelValue\`。

项目里自定义表单组件特别多，这块改起来真的累。

## 值得一提的改进

虽然踩了不少坑，但用了一段时间后，确实感受到了Vue3的好处：

**Composition API真的香**

以前Options API写大组件，各种方法、数据分散在data、methods、computed里，找起来费劲。现在用Composition API，相关逻辑可以组织在一起，清晰多了。

\`\`\`javascript
// 可以把相关逻辑封装成composable
function useUserData() {
  const user = ref(null)
  const loading = ref(false)
  
  async function fetchUser() {
    loading.value = true
    user.value = await api.getUser()
    loading.value = false
  }
  
  return { user, loading, fetchUser }
}
\`\`\`

**性能确实提升了**

打包体积小了30%，首屏加载快了不少。用户感知还挺明显的。

**TypeScript支持好太多了**

以前Vue2配TypeScript各种别扭，现在丝滑很多。代码提示、类型检查都很舒服。

## 给准备迁移的建议

1. **不要急着全部重写**：可以先在新功能里试用Vue3，熟悉了再迁移老代码
2. **重点关注路由和状态管理**：这两块改动最大
3. **利用好官方迁移指南**：Vue官网的迁移文档写得很详细
4. **测试要充分**：我们迁移后发现了不少小问题，幸好QA把关严格

总的来说，升级Vue3是值得的，虽然过程有点痛苦。现在回头看，早升早享受。

你们升级Vue3遇到什么问题了吗？欢迎在评论区交流。
`,
      category: "前端开发",
      tags: ["Vue", "Vue3", "前端"],
      status: "published",
      isFeatured: true,
      views: 1580,
      likes: 89,
      publishedAt: new Date("2025-10-28"),
    },
    // 文章2：工具推荐
    {
      title: "2025年了，我的前端开发工具箱长这样",
      slug: "my-frontend-toolbox-2025",
      excerpt:
        "用了这么多年前端工具，分享一下我现在常用的那些，都是实战中真正提效的。",
      content: `
      经常有人问我用什么工具开发，今天就整理一下我的工具箱。这些都是我每天都在用的，不是那种装了吃灰的。
      
      ## 编辑器：Cursor
      
      今年最大的变化是从VSCode切到了Cursor。
      
      说实话一开始我是拒绝的，VSCode用得好好的，为啥要换？但试了一周后真香了。
      
      **为什么选Cursor：**
      - AI代码补全真的准，比Copilot好用
      - 直接聊天改代码，不用来回切换
      - 还是VSCode的底子，插件都能用
      - 快捷键一样，无缝切换
      
      现在写代码效率至少提升了30%。特别是写一些重复性的代码，AI一下就补全了，爽。
      
      唯一的问题是要付费，但我觉得值。毕竟时间更值钱。
      
      ## 终端：Warp
      
      以前一直用iTerm2，今年换成了Warp。
      
      Warp最大的特点是把命令当成块，可以选中、复制、编辑。还有AI补全命令，记不住的命令直接问，它会给你生成。
      
      比如我想找某个端口占用的进程：
      
      \`\`\`
      直接输入：find process using port 3000
      它自动转成：lsof -i :3000
      \`\`\`
      
      省了好多查命令的时间。
      
      ## 浏览器：Arc
      
      Chrome用了这么多年，今年终于换了。Arc的设计理念挺特别的：
      - 标签页在侧边，垂直排列
      - 自动归档不常用的标签
      - Space功能，不同项目分开
      
      刚开始不太习惯，用了一个月后发现真的回不去了。Chrome的标签栏太乱了。
      
      ## API测试：Hoppscotch
      
      以前用Postman，但越来越臃肿。现在用Hoppscotch（以前叫Postwoman），开源免费，还有网页版。
      
      界面简洁，功能够用，响应速度快。支持GraphQL、WebSocket这些，基本需求都满足。
      
      ## Git工具：Lazygit
      
      命令行Git客户端。
      
      我知道很多人喜欢SourceTree或者GitKraken这种GUI工具，但我更喜欢命令行。Lazygit在终端里就能完成所有Git操作，快捷键用熟了效率很高。
      
      \`\`\`bash
      # 安装
      brew install lazygit
      
      # 使用
      cd your-project
      lazygit
      \`\`\`
      
      ## 数据库工具：TablePlus
      
      以前用Navicat，但太贵了。TablePlus便宜又好用，界面也更现代。
      
      支持MySQL、PostgreSQL、Redis、MongoDB等等，基本覆盖了所有常用数据库。
      
      ## 设计协作：Figma
      
      这个没什么好说的，业界标准了。
      
      作为开发，主要用来：
      - 查看设计稿
      - 复制CSS代码
      - 导出切图
      
      和设计师协作很方便，改动实时同步，不用来回发文件了。
      
      ## 抓包工具：Proxyman
      
      Mac上最好用的抓包工具，比Charles好用。
      
      界面现代，操作简单。支持HTTPS解密，过滤规则很灵活。做移动端调试的时候特别有用。
      
      ## 笔记工具：Notion
      
      代码笔记、项目文档、个人知识库都放Notion里。
      
      以前试过很多工具：印象笔记、有道云、Obsidian等等。最后还是Notion最适合我：
      - 块编辑灵活
      - 支持代码高亮
      - 可以做数据库
      - 协作方便
      
      ## 写在最后
      
      工具只是工具，最重要的还是会用。这些工具都是在实际开发中慢慢积累下来的，每个人的习惯不一样，不一定都适合你。
      
      关键是找到适合自己的，然后熟练掌握。
      
      你还有什么好用的开发工具推荐吗？评论区见。
      `,
      category: "工具推荐",
      tags: ["开发工具", "效率", "编辑器"],
      status: "published",
      isFeatured: true,
      views: 2340,
      likes: 156,
      publishedAt: new Date("2025-10-25"),
    }, // 文章3：技术选型经验
    {
      title: "React还是Vue？我在3个项目后的真实感受",
      slug: "react-vs-vue-real-experience",
      excerpt: "不吹不黑，说说我用React和Vue做了几个项目后的真实体验。",
      content: `
      这个话题老生常谈了，但我觉得还是值得聊聊。因为网上很多对比都太理论了，我想从实际项目的角度说说。
      
      ## 我的经历
      
      先说下背景，我用Vue做了2年，最近一年主要用React。两个都算熟悉，不是那种只会一个就黑另一个的。
      
      去年做了3个项目：
      - 一个后台管理系统（Vue3 + Element Plus）
      - 一个移动端H5（React + Ant Design Mobile）
      - 一个企业官网（Next.js）
      
      ## 开发体验对比
      
      **上手难度**
      
      Vue确实更简单。模板语法很直观，新人看一遍文档就能写。
      React的JSX刚开始会有点绕，特别是一些高级用法。
      
      但说实话，工作一段时间后，这个差异就不明显了。两个都能熟练使用。
      
      **组件写法**
      
      Vue的单文件组件我很喜欢，template、script、style都在一起，清晰。
      
      React全是JS，一开始不太习惯，但现在觉得也挺好。特别是写一些复杂逻辑的时候，JS的灵活性更高。
      
      **状态管理**
      
      Vue有Pinia，React有Zustand、Jotai这些。
      
      坦白说，Pinia确实简单易用。Zustand也不错，但React的状态管理方案太多了，选择恐惧症。
      
      **TypeScript支持**
      
      React + TypeScript真的丝滑。类型推断很准确，写起来很舒服。
      
      Vue3的TS支持比Vue2好多了，但还是感觉React更自然一些。
      
      ## 生态对比
      
      **UI组件库**
      
      Vue：Element Plus、Ant Design Vue、Naive UI
      React：Ant Design、Material-UI、Chakra UI
      
      都够用，但React的选择更多，质量也更高。特别是一些小众需求，React生态里更容易找到轮子。
      
      **第三方库**
      
      React的npm包数量碾压Vue。
      
      做项目的时候深有体会，很多功能React有现成的库，Vue要么没有，要么质量不行。
      
      但反过来说，Vue的官方工具链更完善。脚手架、路由、状态管理都是官方的，统一性好。
      
      ## 性能对比
      
      理论上Vue的响应式更新应该更精准。但实际项目中，差异真的不大。
      
      除非是那种超大数据量的列表渲染，否则感知不到区别。而且优化手段两边都有，虚拟滚动、分页、缓存该用都得用。
      
      ## 实际项目选择
      
      现在接项目我是这么选的：
      
      **选Vue的情况：**
      - 后台管理系统（Element Plus真的好用）
      - 团队主要会Vue
      - 项目周期紧，要快速上线
      
      **选React的情况：**
      - 需要SSR（Next.js很成熟）
      - 需要跨端（React Native）
      - 要用的第三方库只有React版本
      - 团队技术栈是React
      
      ## 个人建议
      
      如果你是新手，我建议先学Vue。上手快，挫败感低，容易建立信心。
      
      但不要只会一个。现在大厂基本都是React，如果想进大厂，React是必备技能。
      
      如果你已经会一个，学另一个也不难。很多概念是通用的，主要是API和写法上的差异。
      
      ## 真实的想法
      
      说实话，现在我觉得框架不是最重要的。重要的是：
      - 能不能写出好维护的代码
      - 能不能解决实际问题
      - 性能优化做得怎么样
      
      框架只是工具，不要被工具限制住。React和Vue都很优秀，选适合项目的就行。
      
      至于那些一天到晚React吹上天或者Vue全宇宙最好的，听听就行了。
      
      能把项目做好，用什么框架都行。
      
      最后，别争了，前端还有Next.js、Remix、Astro、Solid这些新玩意等着学呢😂
      
      你们现在项目用的是React还是Vue？选择的理由是什么？
      `,
      category: "技术分享",
      tags: ["React", "Vue", "前端框架"],
      status: "published",
      isFeatured: true,
      views: 3250,
      likes: 234,
      publishedAt: new Date("2025-10-22"),
    }, // 文章4：踩坑记录
    {
      title: "线上故障复盘：一次因为缓存引发的惨案",
      slug: "production-incident-cache-issue",
      excerpt:
        "上周搞了个大乌龙，用户数据串了。记录下来，希望大家别犯同样的错误。",
      content: `
      上周五晚上10点，正准备下班，突然接到运营的电话：用户反馈看到了别人的订单信息！
      
      当时整个人都懵了，数据泄露这种事情可不是闹着玩的。
      
      ## 问题现象
      
      用户A登录后，看到的订单列表里有用户B的订单。刷新几次后又正常了，过一会儿又出现。
      
      第一反应是数据库查询出问题了，但查了半天SQL没发现问题。
      
      ## 排查过程
      
      **第一步：查日志**
      
      后端日志显示，每次请求的userId都是正确的，SQL查询条件也对。
      
      那为什么会返回错误的数据？
      
      **第二步：检查Redis**
      
      我们的订单列表做了Redis缓存，缓存key是：\`orders:userId\`
      
      检查了一下，key的生成逻辑没问题。但是！！！
      
      突然想起来，上午有个同事改了Redis的配置，把过期时间从5分钟改成了30分钟。
      
      **找到原因了**
      
      问题出在缓存的序列化和反序列化上。
      
      原来的代码是这样的：
      
      \`\`\`javascript
      // 缓存订单列表
      async function getOrders(userId) {
        const cacheKey = \`orders:\${userId}\`;
        
        // 先查缓存
        const cached = await redis.get(cacheKey);
        if (cached) {
          return JSON.parse(cached);
        }
        
        // 查数据库
        const orders = await db.query('SELECT * FROM orders WHERE userId = ?', [userId]);
        
        // 写缓存
        await redis.set(cacheKey, JSON.stringify(orders), 'EX', 300);
        
        return orders;
      }
      \`\`\`
      
      看起来没问题对吧？
      
      但是！在高并发的情况下，可能出现这种情况：
      
      1. 用户A请求进来，缓存未命中
      2. 用户B请求进来，缓存也未命中（因为A还没写入）
      3. A查询数据库，得到A的订单
      4. B查询数据库，得到B的订单
      5. A写缓存，key是\`orders:A\`
      6. B写缓存，但因为某些原因（网络延迟、进程调度等），写入的时候userId变量已经被后续请求修改了，写成了\`orders:A\`
      
      这就导致B的数据覆盖了A的缓存。
      
      ## 解决方案
      
      最终的修复方案是加锁：
      
      \`\`\`javascript
      async function getOrders(userId) {
        const cacheKey = \`orders:\${userId}\`;
        const lockKey = \`lock:\${cacheKey}\`;
        
        // 先查缓存
        let cached = await redis.get(cacheKey);
        if (cached) {
          return JSON.parse(cached);
        }
        
        // 获取锁
        const lock = await redis.set(lockKey, '1', 'EX', 10, 'NX');
        if (!lock) {
          // 没获取到锁，等待一下再查缓存
          await sleep(100);
          cached = await redis.get(cacheKey);
          if (cached) {
            return JSON.parse(cached);
          }
        }
        
        try {
          // 查数据库
          const orders = await db.query('SELECT * FROM orders WHERE userId = ?', [userId]);
          
          // 写缓存
          await redis.set(cacheKey, JSON.stringify(orders), 'EX', 1800);
          
          return orders;
        } finally {
          // 释放锁
          await redis.del(lockKey);
        }
      }
      \`\`\`
      
      ## 教训总结
      
      1. **缓存并发问题不能忽视**
         高并发场景下，看似简单的缓存读写都可能出问题。
      
      2. **加锁要谨慎**
         锁的粒度、过期时间都要仔细考虑。我们这里用的是简单的Redis锁，生产环境建议用Redlock或者其他成熟方案。
      
      3. **监控很重要**
         这次如果不是用户反馈，可能很久才能发现。后来加了数据一致性的监控告警。
      
      4. **测试要充分**
         单元测试覆盖不到这种并发场景，需要做压力测试。
      
      5. **改配置要谨慎**
         同事改个缓存过期时间，结果暴露了代码的并发问题。任何配置修改都要评估影响。
      
      ## 后续优化
      
      除了修复问题，还做了这些优化：
      
      - 加了缓存穿透保护
      - 加了缓存雪崩保护（随机过期时间）
      - 加了数据一致性校验
      - 完善了监控告警
      
      现在看起来，这次故障虽然让人头大，但也让我们的系统更健壮了。
      
      你们遇到过什么奇葩的线上问题？评论区分享一下。
      `,
      category: "踩坑记录",
      tags: ["Redis", "缓存", "线上故障"],
      status: "published",
      isFeatured: false,
      views: 2890,
      likes: 178,
      publishedAt: new Date("2025-10-20"),
    }, // 文章5：实用技巧
    {
      title: "让我提效30%的7个VSCode插件",
      slug: "vscode-plugins-productivity",
      excerpt: "不装一堆插件，只推荐真正用得上的。",
      content: `
      VSCode插件市场有上万个插件，但真正每天都用的可能就那么几个。
      
      今天分享7个让我开发效率提升的插件，都是用了就回不去的那种。
      
      ## 1. Error Lens
      
      **下载量：** 500万+
      **我的评价：** 必装
      
      这个插件把错误和警告直接显示在代码行尾，不用鼠标hover就能看到。
      
      以前看eslint错误，要么看底下的Problems面板，要么鼠标悬停。现在错误直接在代码旁边，一眼就能看到。
      
      红色的错误、黄色的警告，特别醒目。写代码的时候能及时发现问题。
      
      ## 2. Git Graph
      
      **下载量：** 300万+
      **我的评价：** Git可视化最好的
      
      可以在VSCode里看Git提交历史，分支关系一目了然。
      
      支持的操作也很全：
      - 查看某次提交的改动
      - 创建分支
      - 合并分支
      - Cherry-pick
      
      比命令行直观多了，也不用开SourceTree这种独立软件。
      
      ## 3. Thunder Client
      
      **下载量：** 200万+
      **我的评价：** API测试神器
      
      之前测试API都要打开Postman，现在直接在VSCode里就能测。
      
      界面简洁，功能够用：
      - 支持GET、POST、PUT、DELETE等所有方法
      - 可以保存请求，按项目分类
      - 支持环境变量
      - 响应时间、状态码一目了然
      
      对于不复杂的API测试，完全够用了。
      
      ## 4. Todo Tree
      
      **下载量：** 150万+
      **我的评价：** 管理待办利器
      
      会扫描代码里所有的TODO、FIXME、HACK等注释，统一显示在侧边栏。
      
      以前写TODO注释，过几天就忘了。现在一打开侧边栏，所有待办一目了然。
      
      还支持自定义关键词和颜色，我加了个NOTE关键词，专门记录重要的代码说明。
      
      ## 5. CodeSnap
      
      **下载量：** 100万+
      **我的评价：** 代码截图首选
      
      需要分享代码截图的时候（写文章、发朋友圈），用这个生成的图片特别好看。
      
      选中代码，右键选择CodeSnap，就能生成漂亮的代码截图。支持多种主题，还能自定义背景色、阴影等。
      
      比直接截屏专业多了。
      
      ## 6. Import Cost
      
      **下载量：** 300万+
      **我的评价：** 包大小警报器
      
      会在import语句后面显示包的大小。
      
      比如：
      \`\`\`javascript
      import moment from 'moment';  // 💾 72.3KB (gzipped: 18.6KB)
      \`\`\`
      
      看到包很大就会考虑要不要用，或者有没有更轻量的替代品。
      
      做性能优化的时候特别有用。
      
      ## 7. Better Comments
      
      **下载量：** 400万+
      **我的评价：** 注释更清晰
      
      给不同类型的注释加上颜色：
      - \`// TODO\` 橙色
      - \`// FIXME\` 红色
      - \`// NOTE\` 蓝色
      - \`// ?\` 灰色（疑问）
      - \`// !\` 红色（重要）
      
      代码里的注释一眼就能区分重要程度。
      
      ## 为什么只推荐7个
      
      说实话，我以前也装了几十个插件。但后来发现：
      - 插件太多，VSCode会变慢
      - 很多插件功能重复
      - 有些插件装了从来不用
      
      现在的原则是：不常用的不装。这7个是我每天都会用到的。
      
      ## 插件使用建议
      
      1. **定期清理**
         每个月检查一下，不用的就卸载。VSCode快才是真的快。
      
      2. **禁用而不是卸载**
         有些插件只在特定项目用，可以禁用，需要时再启用。
      
      3. **看文档**
         很多插件功能很强大，但你可能只用了10%。花点时间看看文档，物尽其用。
      
      4. **快捷键要记**
         常用的插件一定要记住快捷键，用鼠标点菜单太慢了。
      
      ## 你的必装插件
      
      这是我的清单，你的呢？
      
      评论区说说你每天都在用的插件，大家交流一下。
      `,
      category: "工具推荐",
      tags: ["VSCode", "插件", "效率"],
      status: "published",
      isFeatured: false,
      views: 4120,
      likes: 289,
      publishedAt: new Date("2025-10-18"),
    },
        // 文章6：AI工具实战
        {
            title: 'GitHub Copilot用了半年，说说它到底值不值',
            slug: 'github-copilot-review-after-6-months',
            excerpt: '从刚开始的惊艳到现在的日常，我对Copilot的看法变了好几次。',
            content: `
      去年5月公司给买了GitHub Copilot，到现在用了半年多。看到很多人在纠结要不要订阅，说说我的真实体验。
      
      ## 刚开始：哇，这也太神了！
      
      第一次用的时候确实惊艳。
      
      写一个函数名，它就能猜出你要做什么，直接给你补全整个函数。写注释，它能根据注释生成代码。
      
      我记得第一个让我惊讶的场景是写日期格式化函数。我刚写了个注释：
      
      \`\`\`javascript
      // 格式化日期为 YYYY-MM-DD HH:mm:ss
      \`\`\`
      
      按下回车，它直接给我生成了完整的函数：
      
      \`\`\`javascript
      function formatDate(date) {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        const hours = String(date.getHours()).padStart(2, '0');
        const minutes = String(date.getMinutes()).padStart(2, '0');
        const seconds = String(date.getSeconds()).padStart(2, '0');
        return \`\${year}-\${month}-\${day} \${hours}:\${minutes}:\${seconds}\`;
      }
      \`\`\`
      
      这不就是我想要的吗？那一刻感觉AI要统治世界了。
      
      ## 一个月后：发现问题
      
      用了一段时间后，开始发现一些问题：
      
      **1. 有时候会瞎补全**
      
      比如我要写个API调用，它给我补全了个不存在的接口。或者用了我项目里根本没有的库。
      
      刚开始我还以为是我配置有问题，后来发现它就是会瞎猜。
      
      **2. 代码质量参差不齐**
      
      有时候生成的代码很优雅，有时候就很垃圾。
      
      见过它生成的代码里有：
      - 冗余的判断
      - 不必要的变量
      - 过时的写法
      - 甚至错误的逻辑
      
      完全不能无脑接受，必须仔细review。
      
      **3. 影响思考**
      
      这个是最大的问题。
      
      刚开始我特别依赖它，遇到问题就等它补全。结果发现自己的思考能力在退化，写代码越来越机械。
      
      有次它给的建议我觉得不对，但又说不出哪里不对。后来才意识到，我已经习惯了接受它的建议，而不是自己思考。
      
      ## 三个月后：找到平衡
      
      慢慢地，我找到了和Copilot相处的方式：
      
      **只在特定场景下用：**
      
      1. **写模板代码**
         比如CRUD、表单验证这种重复性代码，让它写省时间。
      
      2. **写测试用例**
         测试代码往往比较固定，Copilot生成的质量还不错。
      
      3. **写正则表达式**
         我承认我不太会正则，让它帮忙写，然后去测试一下。
      
      4. **写注释补全代码**
         先写好注释说明逻辑，让它根据注释生成代码。
      
      **不用的场景：**
      
      1. **核心业务逻辑**
         这种必须自己写，不能偷懒。
      
      2. **性能敏感的代码**
         Copilot生成的代码不一定最优，需要自己优化。
      
      3. **安全相关的代码**
         不能冒险，必须自己写。
      
      ## 现在：成为工具箱的一部分
      
      现在Copilot对我来说就是个工具，不是全部。
      
      就像以前写代码会用StackOverflow、GitHub搜索一样，现在多了Copilot这个选项。
      
      **效率提升了多少？**
      
      很难量化，但我感觉大概提升了20%左右。主要是减少了写重复代码的时间。
      
      但这个提升不是直接的。不是说以前100行代码要写1小时，现在只要48分钟。
      
      而是：
      - 写测试用例更快了
      - 写注释更认真了（因为可以用来生成代码）
      - 写CRUD这种无脑代码不用动脑了
      
      ## 值不值这个钱？
      
      Copilot个人版是每月10美元，一年120美元。
      
      我的看法是：
      
      **对于公司：** 值得。员工效率提升20%，这个投入很划算。
      
      **对于个人：** 看情况
      - 如果你经常写代码，值得
      - 如果只是偶尔写，可能不太值
      - 如果是学生，有教育优惠，强烈建议用
      
      **对于新手：** 慎用
      新手还是应该多自己写，培养编程思维。过度依赖Copilot会影响学习。
      
      ## 替代品
      
      如果觉得Copilot贵，可以试试这些：
      
      **Tabnine** - 免费版就能用，补全准确度还行
      **Codeium** - 完全免费，支持多种编辑器
      **Cursor** - 我现在在用，体验比Copilot好
      
      特别是Cursor，虽然要付费，但我觉得比单独买Copilot更值。
      
      ## 总结
      
      半年用下来，我的态度从：
      - 一开始的惊艳
      - 到中期的怀疑
      - 到现在的平和
      
      Copilot确实有用，但不是银弹。它是个工具，要学会正确使用。
      
      关键是保持思考能力，不要让AI替代你的大脑。
      
      你用Copilot或其他AI编程工具吗？体验怎么样？评论区聊聊。
      `,
            category: 'AI工具',
            tags: ['GitHub Copilot', 'AI编程', '效率工具'],
            status: 'published',
            isFeatured: true,
            views: 5230,
            likes: 412,
            publishedAt: new Date('2025-10-15'),
          },    // 文章7：后端技术选型
          {
            title: 'Node.js做后端一年了，说说踩过的坑',
            slug: 'nodejs-backend-one-year-experience',
            excerpt: '从Java转Node.js，体验了一把前后端统一的快感，也踩了不少坑。',
            content: `
      我之前是写Java后端的，去年项目需要，开始用Node.js。现在用了快一年，说说真实感受。
      
      ## 为什么选Node.js
      
      当时选Node.js主要考虑：
      
      1. **团队都会JavaScript**
         不用再单独招后端，前端也能写接口，人力成本低。
      
      2. **开发效率高**
         没有编译过程，改完代码立即生效。不像Java还要重启服务器。
      
      3. **生态丰富**
         npm包超级多，各种轮子都有。
      
      4. **性能还行**
         对于IO密集型应用，Node.js性能完全够用。
      
      ## 刚开始的蜜月期
      
      一开始确实很爽。
      
      写个Express接口，几行代码就搞定：
      
      \`\`\`javascript
      app.get('/api/users', async (req, res) => {
        const users = await User.find();
        res.json(users);
      });
      \`\`\`
      
      比Java的Spring Boot简洁太多了。不用写一堆注解、配置、实体类。
      
      而且调试也方便，console.log一把梭，改完代码刷新就能看到效果。
      
      ## 一个月后：开始遇到问题
      
      **异步地狱**
      
      Node.js最大的特点是异步，但也是最大的坑。
      
      刚开始不熟悉，写了一堆回调套回调：
      
      \`\`\`javascript
      getUserById(id, (err, user) => {
        if (err) return handleError(err);
        getOrdersByUser(user.id, (err, orders) => {
          if (err) return handleError(err);
          getOrderDetails(orders[0].id, (err, details) => {
            if (err) return handleError(err);
            // 终于拿到数据了...
          });
        });
      });
      \`\`\`
      
      后来学会了async/await，好多了。但还是要小心，忘记await就会出问题。
      
      **错误处理很麻烦**
      
      Java有try-catch可以统一处理异常。Node.js的错误处理就很分散。
      
      既要处理同步错误，又要处理异步错误，还要处理Promise的reject。一不小心就漏掉了。
      
      **类型不安全**
      
      JavaScript是弱类型，经常出现：
      - 传参类型错误
      - 返回值类型不对
      - 拼写错误（变量名写错了）
      
      后来引入了TypeScript，情况好了很多。但TypeScript又有学习成本，类型定义写起来也费时间。
      
      ## 三个月后：生产环境问题
      
      上线后遇到了更多问题：
      
      **内存泄漏**
      
      Node.js的垃圾回收不如Java成熟。不小心就会内存泄漏。
      
      我们遇到过几次：
      - 闭包导致的内存泄漏
      - 事件监听器没有移除
      - 大对象没有及时清理
      
      每次都要用node-clinic、heapdump这些工具排查，很费劲。
      
      **单线程限制**
      
      Node.js是单线程的，CPU密集型任务会阻塞。
      
      有次要处理大量数据计算，直接把服务器卡死了。后来用了worker_threads才解决。
      
      **依赖包的坑**
      
      npm包虽然多，但质量参差不齐。
      
      遇到过：
      - 包突然不维护了
      - 包有安全漏洞
      - 包的依赖版本冲突
      - node_modules巨大无比
      
      每次npm install都是一次冒险。
      
      ## 现在：找到了正确的用法
      
      用了一年，总结出一些经验：
      
      **Node.js适合的场景：**
      
      1. **API服务**
         做RESTful API，Node.js效率很高。
      
      2. **实时应用**
         WebSocket、SSE这种实时通信，Node.js天生适合。
      
      3. **中间层**
         BFF（Backend For Frontend），用Node.js做中间层转发很合适。
      
      4. **工具开发**
         CLI工具、构建工具，Node.js生态好。
      
      **不适合的场景：**
      
      1. **CPU密集型**
         大量计算，还是用Go、Rust这种更合适。
      
      2. **对性能要求极高**
         高并发、低延迟的场景，用Java、Go更稳。
      
      3. **大型系统**
         几十万行代码的系统，维护成本很高。
      
      ## 实用建议
      
      如果你也要用Node.js做后端，建议：
      
      **1. 一定要用TypeScript**
      类型检查能避免很多低级错误。
      
      **2. 选好框架**
      Express太简单了，大项目推荐NestJS或Fastify。
      
      **3. 做好错误处理**
      用统一的错误处理中间件，不要漏掉任何错误。
      
      **4. 注意性能监控**
      用PM2、Clinic等工具监控性能，及时发现问题。
      
      **5. 控制依赖**
      不要什么都用npm包，有些简单功能自己写更好。
      
      ## 会不会后悔
      
      说实话，不后悔。
      
      虽然踩了不少坑，但Node.js确实提高了开发效率。而且现在团队不用再分前端后端，沟通成本低多了。
      
      但如果是大型项目，我还是会考虑Java或Go。Node.js更适合中小型项目。
      
      你们用Node.js做后端吗？遇到过什么坑？
      `,
            category: '后端技术',
            tags: ['Node.js', '后端', 'JavaScript'],
            status: 'published',
            isFeatured: false,
            views: 3680,
            likes: 267,
            publishedAt: new Date('2025-10-12'),
          },    // 文章8：数据库优化
          {
            title: '一条SQL优化，从30秒到0.3秒',
            slug: 'sql-optimization-from-30s-to-0.3s',
            excerpt: '记录一次SQL慢查询优化过程，最终性能提升了100倍。',
            content: `
      上个月接到用户投诉，说订单列表加载太慢。我一查，好家伙，30秒才能出来。这谁受得了？
      
      ## 问题SQL
      
      有问题的SQL大概长这样（简化版）：
      
      \`\`\`sql
      SELECT 
        o.*,
        u.nickname,
        u.avatar,
        p.name as product_name,
        p.image
      FROM orders o
      LEFT JOIN users u ON o.user_id = u.id
      LEFT JOIN order_items oi ON oi.order_id = o.id
      LEFT JOIN products p ON oi.product_id = p.id
      WHERE o.status IN ('pending', 'paid', 'shipped')
        AND o.created_at >= '2024-01-01'
      ORDER BY o.created_at DESC
      LIMIT 20;
      \`\`\`
      
      看起来很普通对吧？但执行要30秒。
      
      ## 排查过程
      
      **第一步：EXPLAIN分析**
      
      \`\`\`sql
      EXPLAIN SELECT ...
      \`\`\`
      
      结果显示：
      - orders表全表扫描，扫了50万行
      - 没有用到索引
      - 临时表，文件排序
      
      问题找到了：没有合适的索引。
      
      **第二步：查看现有索引**
      
      \`\`\`sql
      SHOW INDEX FROM orders;
      \`\`\`
      
      只有一个主键索引，其他啥都没有。
      
      难怪这么慢。
      
      ## 优化方案
      
      **优化1：添加索引**
      
      根据WHERE和ORDER BY条件，添加组合索引：
      
      \`\`\`sql
      ALTER TABLE orders 
      ADD INDEX idx_status_created (status, created_at);
      \`\`\`
      
      执行时间：30秒 → 8秒
      
      有效果，但还不够。
      
      **优化2：避免SELECT ***
      
      \`SELECT *\` 会查询所有字段，包括一些大字段（TEXT、BLOB）。
      
      改成只查需要的字段：
      
      \`\`\`sql
      SELECT 
        o.id,
        o.order_no,
        o.status,
        o.total_amount,
        o.created_at,
        u.nickname,
        u.avatar
      FROM ...
      \`\`\`
      
      执行时间：8秒 → 3秒
      
      继续优化。
      
      **优化3：优化JOIN**
      
      原来的SQL有个问题：order_items是一对多关系，一个订单可能有多个商品。
      
      这样JOIN会产生笛卡尔积，数据量爆炸。
      
      改成分两次查询：
      
      \`\`\`sql
      -- 第一次：查订单基本信息
      SELECT 
        o.id,
        o.order_no,
        o.status,
        o.total_amount,
        o.created_at,
        u.nickname,
        u.avatar
      FROM orders o
      LEFT JOIN users u ON o.user_id = u.id
      WHERE o.status IN ('pending', 'paid', 'shipped')
        AND o.created_at >= '2024-01-01'
      ORDER BY o.created_at DESC
      LIMIT 20;
      
      -- 第二次：根据订单ID查商品信息
      SELECT 
        oi.order_id,
        p.name,
        p.image
      FROM order_items oi
      LEFT JOIN products p ON oi.product_id = p.id
      WHERE oi.order_id IN (1,2,3,...);  -- 第一次查询的订单ID
      \`\`\`
      
      在代码里把两次查询的结果组装起来。
      
      执行时间：3秒 → 0.5秒
      
      好多了！
      
      **优化4：添加覆盖索引**
      
      发现users表的查询还在扫描主键索引。因为要查nickname和avatar字段。
      
      创建覆盖索引：
      
      \`\`\`sql
      ALTER TABLE users 
      ADD INDEX idx_id_nickname_avatar (id, nickname, avatar);
      \`\`\`
      
      执行时间：0.5秒 → 0.3秒
      
      完美！
      
      ## 最终效果
      
      - 优化前：30秒
      - 优化后：0.3秒
      - 性能提升：100倍
      
      用户反馈：快得飞起！
      
      ## 经验总结
      
      **1. 一定要加索引**
      
      特别是WHERE、ORDER BY、JOIN用到的字段。
      
      但也不是越多越好，索引会影响写入性能。根据实际查询场景来。
      
      **2. 避免SELECT ***
      
      只查需要的字段，能显著减少数据传输量。
      
      **3. 注意JOIN的性能**
      
      一对多的JOIN要小心，可能导致数据量爆炸。
      
      可以考虑：
      - 分多次查询
      - 用子查询
      - 反范式设计（冗余字段）
      
      **4. 用EXPLAIN分析**
      
      不要瞎猜，用EXPLAIN看执行计划。
      
      看这几个关键指标：
      - type：是否全表扫描
      - rows：扫描了多少行
      - Extra：是否有临时表、文件排序
      
      **5. 分页要优化**
      
      LIMIT 10000, 20这种深度分页很慢。
      
      可以改成：
      \`\`\`sql
      WHERE id > last_id 
      ORDER BY id 
      LIMIT 20
      \`\`\`
      
      ## 预防措施
      
      优化完这个问题，我们做了这些改进：
      
      1. **慢查询日志**
         开启MySQL慢查询日志，超过1秒的查询都记录下来。
      
      2. **监控告警**
         慢查询超过阈值就告警，及时发现问题。
      
      3. **代码Review**
         新写的SQL都要review，避免类似问题。
      
      4. **定期优化**
         每个月检查一次慢查询，主动优化。
      
      数据库优化是个持续的过程，不能等出问题了再优化。
      
      你们遇到过什么SQL性能问题？怎么解决的？
      `,
            category: '数据库',
            tags: ['MySQL', 'SQL优化', '性能优化'],
            status: 'published',
            isFeatured: false,
            views: 4560,
            likes: 334,
            publishedAt: new Date('2025-10-08'),
          },
              // 文章9：部署经验
    {
        title: 'Docker部署踩坑记：为什么容器总是莫名其妙挂掉',
        slug: 'docker-deployment-troubleshooting',
        excerpt: '第一次用Docker部署项目，遇到了各种奇怪的问题。记录下来，希望后来人少走弯路。',
        content: `
  公司之前的项目都是直接部署在服务器上，这次新项目决定用Docker。我作为负责人，开始了漫长的踩坑之旅。
  
  ## 问题1：容器启动就退出
  
  第一次docker run，满怀期待，结果：
  
  \`\`\`bash
  docker run -d my-app
  # 查看状态
  docker ps
  # 结果：没有运行的容器
  \`\`\`
  
  查日志：
  
  \`\`\`bash
  docker logs <container-id>
  # npm: not found
  \`\`\`
  
  原来是镜像里没有npm。
  
  **原因：** 我用的基础镜像是\`node:alpine\`，太精简了，连npm都没有。
  
  **解决：** 换成\`node:18\`完整镜像，或者自己安装npm。
  
  \`\`\`dockerfile
  FROM node:18-alpine
  RUN apk add --no-cache npm
  \`\`\`
  
  ## 问题2：环境变量不生效
  
  本地测试好好的，放到Docker里就连不上数据库。
  
  检查了半天发现：环境变量没传进去。
  
  **错误做法：**
  
  \`\`\`bash
  docker run -d my-app
  \`\`\`
  
  **正确做法：**
  
  \`\`\`bash
  docker run -d \
    -e DB_HOST=mysql \
    -e DB_PORT=3306 \
    -e DB_NAME=mydb \
    my-app
  \`\`\`
  
  或者用\`--env-file\`：
  
  \`\`\`bash
  docker run -d --env-file .env my-app
  \`\`\`
  
  后来改用docker-compose，配置更清晰：
  
  \`\`\`yaml
  version: '3.8'
  services:
    app:
      image: my-app
      env_file:
        - .env
  \`\`\`
  
  ## 问题3：文件权限问题
  
  容器里创建的文件，宿主机上没权限访问。
  
  比如日志文件，容器里是root创建的，宿主机上普通用户读不了。
  
  **解决：** 指定运行用户
  
  \`\`\`dockerfile
  FROM node:18-alpine
  
  # 创建非root用户
  RUN addgroup -g 1001 -S nodejs
  RUN adduser -S nodejs -u 1001
  
  # 切换用户
  USER nodejs
  
  COPY --chown=nodejs:nodejs . .
  CMD ["node", "index.js"]
  \`\`\`
  
  ## 问题4：容器占用空间越来越大
  
  跑了一段时间后，服务器硬盘快满了。
  
  查看Docker占用：
  
  \`\`\`bash
  docker system df
  # Images        30GB
  # Containers    10GB
  # Volumes       5GB
  # Build Cache   15GB
  \`\`\`
  
  60GB！
  
  **原因：**
  - 旧的镜像没删除
  - 停止的容器还在
  - 构建缓存太多
  
  **解决：** 定期清理
  
  \`\`\`bash
  # 删除停止的容器
  docker container prune
  
  # 删除未使用的镜像
  docker image prune -a
  
  # 删除未使用的卷
  docker volume prune
  
  # 一键清理所有
  docker system prune -a --volumes
  \`\`\`
  
  我写了个定时任务，每周自动清理一次。
  
  ## 问题5：日志爆炸
  
  有次容器突然挂了，查原因发现日志文件占了50GB。
  
  Docker默认会保存所有stdout输出，不限制大小。
  
  **解决：** 限制日志大小
  
  \`\`\`json
  // /etc/docker/daemon.json
  {
    "log-driver": "json-file",
    "log-opts": {
      "max-size": "10m",
      "max-file": "3"
    }
  }
  \`\`\`
  
  或者在docker-compose里配置：
  
  \`\`\`yaml
  services:
    app:
      logging:
        driver: "json-file"
        options:
          max-size: "10m"
          max-file: "3"
  \`\`\`
  
  ## 问题6：容器之间网络不通
  
  多容器部署，容器之间访问不了。
  
  比如Node.js容器访问MySQL容器，连不上。
  
  **原因：** 没有用自定义网络，容器在不同网络里。
  
  **解决：** 用docker-compose自动创建网络
  
  \`\`\`yaml
  version: '3.8'
  services:
    app:
      image: my-app
      networks:
        - mynetwork
    
    mysql:
      image: mysql:8
      networks:
        - mynetwork
  
  networks:
    mynetwork:
      driver: bridge
  \`\`\`
  
  或者手动创建网络：
  
  \`\`\`bash
  docker network create mynetwork
  docker run --network mynetwork --name mysql mysql:8
  docker run --network mynetwork --name app my-app
  \`\`\`
  
  ## 问题7：时区不对
  
  容器里的时间比实际时间晚8小时。
  
  日志时间、定时任务都不对。
  
  **解决：** 设置时区
  
  \`\`\`dockerfile
  FROM node:18-alpine
  
  # 设置时区
  RUN apk add --no-cache tzdata
  ENV TZ=Asia/Shanghai
  
  CMD ["node", "index.js"]
  \`\`\`
  
  或者挂载宿主机时区：
  
  \`\`\`bash
  docker run -v /etc/localtime:/etc/localtime:ro my-app
  \`\`\`
  
  ## 经验总结
  
  **1. 镜像要精简**
  - 用alpine减少体积
  - 多阶段构建
  - 只复制必要的文件
  
  \`\`\`dockerfile
  # 构建阶段
  FROM node:18 AS builder
  WORKDIR /app
  COPY package*.json ./
  RUN npm ci --only=production
  
  # 运行阶段
  FROM node:18-alpine
  WORKDIR /app
  COPY --from=builder /app/node_modules ./node_modules
  COPY . .
  CMD ["node", "index.js"]
  \`\`\`
  
  **2. 用docker-compose**
  管理多容器更方便，配置更清晰。
  
  **3. 持久化数据要用卷**
  数据库、文件上传等，一定要用volume，不然容器删了数据就没了。
  
  **4. 健康检查**
  加上HEALTHCHECK，容器挂了能自动重启。
  
  \`\`\`dockerfile
  HEALTHCHECK --interval=30s --timeout=3s \
    CMD node healthcheck.js || exit 1
  \`\`\`
  
  **5. 日志要重定向**
  不要让所有日志都输出到stdout，重定向到文件或日志服务。
  
  ## 现在的部署流程
  
  经过这么多踩坑，现在的部署流程已经很成熟了：
  
  1. 本地开发用docker-compose
  2. CI/CD自动构建镜像
  3. 推送到私有仓库
  4. 生产环境自动拉取部署
  5. 监控告警自动通知
  
  从手忙脚乱到自动化部署，花了3个月时间。但值得，现在部署一个新项目只要10分钟。
  
  你们用Docker遇到过什么坑？评论区交流一下。
  `,
        category: 'DevOps',
        tags: ['Docker', '部署', '运维'],
        status: 'published',
        isFeatured: false,
        views: 3890,
        likes: 298,
        publishedAt: new Date('2025-10-05'),
      },
      {
        title: '前端性能优化：我是如何让首屏加载提速300%的',
        slug: 'frontend-performance-optimization-guide',
        excerpt: '记录一次完整的前端性能优化过程，从8秒到2秒，用户体验质的飞跃。',
        category: '性能优化',
        tags: ['性能优化', '前端', 'Web Vitals'],
        status: 'published',
        isFeatured: true,
        views: 2850,
        likes: 215,
        publishedAt: new Date('2025-10-03'),
        content: `
      去年接手一个老项目，用户反馈首屏加载太慢。打开控制台一看，8秒才能交互，这谁受得了？
      
      ## 问题分析
      
      用Chrome DevTools的Lighthouse跑了一遍，分数惨不忍睹：
      
      - Performance: 28分
      - FCP (首次内容绘制): 4.2s
      - LCP (最大内容绘制): 8.1s
      - TBT (总阻塞时间): 1850ms
      
      主要问题：
      - bundle.js 体积 2.8MB
      - 首屏加载30个请求
      - 图片未压缩，单张500KB+
      - 没有任何缓存策略
      
      ## 优化过程
      
      ### 1. 代码分割和懒加载
      
      **原来的写法：**
      
      \`\`\`javascript
      import { BrowserRouter, Routes, Route } from 'react-router-dom';
      import Home from './pages/Home';
      import About from './pages/About';
      import Dashboard from './pages/Dashboard';
      // ... 还有20个页面
      
      function App() {
        return (
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/dashboard" element={<Dashboard />} />
            {/* ... */}
          </Routes>
        );
      }
      \`\`\`
      
      **优化后：**
      
      \`\`\`javascript
      import { lazy, Suspense } from 'react';
      
      // 路由级别懒加载
      const Home = lazy(() => import('./pages/Home'));
      const About = lazy(() => import('./pages/About'));
      const Dashboard = lazy(() => import('./pages/Dashboard'));
      
      function App() {
        return (
          <Suspense fallback={<Loading />}>
            <Routes>
              <Route path="/" element={<Home />} />
              <Route path="/about" element={<About />} />
              <Route path="/dashboard" element={<Dashboard />} />
            </Routes>
          </Suspense>
        );
      }
      \`\`\`
      
      **效果**：bundle.js从2.8MB降到800KB，其他页面按需加载。
      
      ### 2. 图片优化
      
      三板斧：压缩、WebP、懒加载
      
      \`\`\`jsx
      // 安装 react-lazy-load-image-component
      import { LazyLoadImage } from 'react-lazy-load-image-component';
      
      function ProductCard({ product }) {
        return (
          <LazyLoadImage
            src={product.image}
            alt={product.name}
            effect="blur"
            width={300}
            height={200}
          />
        );
      }
      \`\`\`
      
      配合服务端：
      
      \`\`\`nginx
      # Nginx 自动转换 WebP
      location ~* \.(jpg|jpeg|png)$ {
        add_header Vary Accept;
        try_files $uri$webp_suffix $uri =404;
      }
      \`\`\`
      
      **效果**：图片体积减少70%，首屏图片加载从3秒降到1秒。
      
      ### 3. 依赖优化
      
      检查 bundle-analyzer，发现几个大户：
      
      \`\`\`bash
      npm install -D webpack-bundle-analyzer
      \`\`\`
      
      \`\`\`javascript
      // webpack.config.js
      const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
      
      module.exports = {
        plugins: [
          new BundleAnalyzerPlugin()
        ]
      }
      \`\`\`
      
      发现问题：
      - moment.js (200KB) → 换成 dayjs (2KB)
      - lodash (70KB) → 按需引入
      - antd 全量引入 → 按需引入
      
      \`\`\`javascript
      // 优化前
      import moment from 'moment';
      import _ from 'lodash';
      import { Button, Table, Modal, Form } from 'antd';
      
      // 优化后
      import dayjs from 'dayjs';
      import debounce from 'lodash/debounce';
      import Button from 'antd/es/button';
      import Table from 'antd/es/table';
      \`\`\`
      
      **效果**：依赖体积减少400KB。
      
      ### 4. 缓存策略
      
      \`\`\`nginx
      # Nginx 静态资源缓存
      location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
      }
      
      # HTML 不缓存
      location ~* \.html$ {
        add_header Cache-Control "no-cache";
      }
      \`\`\`
      
      配合构建时加hash：
      
      \`\`\`javascript
      // vite.config.js
      export default {
        build: {
          rollupOptions: {
            output: {
              chunkFileNames: 'js/[name]-[hash].js',
              entryFileNames: 'js/[name]-[hash].js',
              assetFileNames: '[ext]/[name]-[hash].[ext]'
            }
          }
        }
      }
      \`\`\`
      
      ### 5. 预加载关键资源
      
      \`\`\`html
      <head>
        <!-- 预加载字体 -->
        <link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
        
        <!-- 预连接第三方域名 -->
        <link rel="preconnect" href="https://cdn.example.com">
        <link rel="dns-prefetch" href="https://api.example.com">
        
        <!-- 预加载关键CSS -->
        <link rel="preload" href="/css/critical.css" as="style">
      </head>
      \`\`\`
      
      ### 6. 服务端渲染（SSR）
      
      对于首屏内容，改用SSR：
      
      \`\`\`javascript
      // Next.js
      export async function getServerSideProps() {
        const products = await fetchProducts();
        
        return {
          props: { products }
        };
      }
      
      export default function Home({ products }) {
        return (
          <div>
            {products.map(product => (
              <ProductCard key={product.id} product={product} />
            ))}
          </div>
        );
      }
      \`\`\`
      
      ### 7. 虚拟滚动
      
      长列表改用虚拟滚动：
      
      \`\`\`jsx
      import { FixedSizeList } from 'react-window';
      
      function ProductList({ products }) {
        const Row = ({ index, style }) => (
          <div style={style}>
            <ProductCard product={products[index]} />
          </div>
        );
      
        return (
          <FixedSizeList
            height={600}
            itemCount={products.length}
            itemSize={120}
            width="100%"
          >
            {Row}
          </FixedSizeList>
        );
      }
      \`\`\`
      
      ## 最终效果
      
      优化后的指标：
      
      - Performance: 28分 → 92分
      - FCP: 4.2s → 1.1s
      - LCP: 8.1s → 2.3s
      - TBT: 1850ms → 180ms
      - Bundle大小: 2.8MB → 800KB
      
      **用户反馈**：页面打开速度明显提升，体验好多了！
      
      ## 性能优化清单
      
      我整理了一个检查清单，每次新项目都会过一遍：
      
      **代码层面**：
      - [ ] 路由懒加载
      - [ ] 组件按需引入
      - [ ] Tree Shaking
      - [ ] 代码分割
      - [ ] 移除console.log
      
      **资源层面**：
      - [ ] 图片压缩
      - [ ] WebP格式
      - [ ] 图片懒加载
      - [ ] 字体子集化
      - [ ] SVG优化
      
      **网络层面**：
      - [ ] HTTP/2
      - [ ] Gzip/Brotli压缩
      - [ ] CDN加速
      - [ ] 缓存策略
      - [ ] 预加载/预连接
      
      **渲染层面**：
      - [ ] SSR/SSG
      - [ ] 虚拟滚动
      - [ ] 防抖节流
      - [ ] 避免重排重绘
      
      **监控层面**：
      - [ ] Lighthouse CI
      - [ ] 真实用户监控(RUM)
      - [ ] 性能预算
      - [ ] 告警机制
      
      ## 工具推荐
      
      - **Lighthouse**: 性能分析
      - **WebPageTest**: 详细的加载瀑布图
      - **webpack-bundle-analyzer**: 打包分析
      - **React DevTools Profiler**: React性能分析
      - **Chrome DevTools Performance**: 运行时性能
      
      性能优化是个持续的过程，不能一劳永逸。建议每个月复盘一次，保持警惕。
      
      你们项目的首屏加载时间多少？有什么优化经验？
      `
      },
      {
        title: '从单体到微服务：我们踩过的坑和经验',
        slug: 'monolith-to-microservices-journey',
        excerpt: '团队花了半年时间做微服务拆分，分享一些实战经验和教训。',
        category: '架构设计',
        tags: ['微服务', '架构', '后端'],
        status: 'published',
        isFeatured: true,
        views: 3120,
        likes: 245,
        publishedAt: new Date('2025-10-01'),
        content: `
      公司业务快速发展，原来的单体应用越来越难维护。老板拍板：微服务改造！
      
      ## 为什么要拆分
      
      原来的单体应用问题：
      - 代码库30万行，改个bug要找半天
      - 部署一次要1小时，出问题全站挂
      - 团队20人，经常代码冲突
      - 扩容只能整体扩，成本高
      
      ## 拆分策略
      
      ### 1. 按业务领域拆分（DDD）
      
      我们采用领域驱动设计（DDD）：
      
      \`\`\`
      用户服务 (User Service)
      ├── 用户注册/登录
      ├── 用户资料管理
      └── 用户权限
      
      订单服务 (Order Service)
      ├── 下单
      ├── 订单查询
      └── 订单状态管理
      
      商品服务 (Product Service)
      ├── 商品管理
      ├── 库存管理
      └── 商品搜索
      
      支付服务 (Payment Service)
      ├── 支付下单
      ├── 支付回调
      └── 退款处理
      \`\`\`
      
      ### 2. 服务间通信
      
      **同步通信 - REST API**：
      
      \`\`\`javascript
      // 订单服务调用商品服务
      async function createOrder(userId, productId, quantity) {
        // 1. 调用商品服务检查库存
        const product = await fetch(\`http://product-service/api/products/\${productId}\`)
          .then(res => res.json());
        
        if (product.stock < quantity) {
          throw new Error('库存不足');
        }
        
        // 2. 调用用户服务验证用户
        const user = await fetch(\`http://user-service/api/users/\${userId}\`)
          .then(res => res.json());
        
        // 3. 创建订单
        const order = await Order.create({
          userId,
          productId,
          quantity,
          amount: product.price * quantity
        });
        
        return order;
      }
      \`\`\`
      
      问题：服务依赖多了，一个服务挂了影响其他服务。
      
      **异步通信 - 消息队列**：
      
      \`\`\`javascript
      // 使用RabbitMQ
      const amqp = require('amqplib');
      
      // 订单服务：发布订单创建事件
      async function publishOrderCreated(order) {
        const connection = await amqp.connect('amqp://localhost');
        const channel = await connection.createChannel();
        
        const exchange = 'order_events';
        await channel.assertExchange(exchange, 'fanout', { durable: true });
        
        channel.publish(
          exchange,
          '',
          Buffer.from(JSON.stringify({
            event: 'order.created',
            data: order
          }))
        );
      }
      
      // 库存服务：订阅订单事件
      async function subscribeOrderEvents() {
        const connection = await amqp.connect('amqp://localhost');
        const channel = await connection.createChannel();
        
        const exchange = 'order_events';
        const queue = 'inventory_service_queue';
        
        await channel.assertQueue(queue, { durable: true });
        await channel.bindQueue(queue, exchange, '');
        
        channel.consume(queue, async (msg) => {
          const { event, data } = JSON.parse(msg.content.toString());
          
          if (event === 'order.created') {
            // 扣减库存
            await reduceStock(data.productId, data.quantity);
          }
          
          channel.ack(msg);
        });
      }
      \`\`\`
      
      ### 3. 服务注册与发现
      
      用Consul做服务注册：
      
      \`\`\`javascript
      const consul = require('consul')();
      
      // 服务启动时注册
      async function registerService() {
        await consul.agent.service.register({
          name: 'order-service',
          id: 'order-service-1',
          address: 'localhost',
          port: 3001,
          check: {
            http: 'http://localhost:3001/health',
            interval: '10s'
          }
        });
      }
      
      // 调用其他服务
      async function callUserService(userId) {
        // 从Consul获取服务地址
        const services = await consul.health.service('user-service');
        const service = services[0];
        
        const url = \`http://\${service.Service.Address}:\${service.Service.Port}\`;
        return fetch(\`\${url}/api/users/\${userId}\`);
      }
      \`\`\`
      
      ### 4. API网关
      
      统一入口，用Kong或自建网关：
      
      \`\`\`javascript
      // 简单的API网关示例
      const express = require('express');
      const { createProxyMiddleware } = require('http-proxy-middleware');
      
      const app = express();
      
      // 路由转发
      app.use('/api/users', createProxyMiddleware({ 
        target: 'http://user-service:3001',
        changeOrigin: true 
      }));
      
      app.use('/api/orders', createProxyMiddleware({ 
        target: 'http://order-service:3002',
        changeOrigin: true 
      }));
      
      app.use('/api/products', createProxyMiddleware({ 
        target: 'http://product-service:3003',
        changeOrigin: true 
      }));
      
      // 统一认证
      app.use((req, res, next) => {
        const token = req.headers.authorization;
        if (!token) {
          return res.status(401).json({ error: 'Unauthorized' });
        }
        // 验证token
        next();
      });
      
      app.listen(3000);
      \`\`\`
      
      ## 踩过的坑
      
      ### 1. 分布式事务
      
      **问题**：用户下单，扣库存和创建订单两个操作，可能一个成功一个失败。
      
      **解决**：用Saga模式
      
      \`\`\`javascript
      // 订单Saga
      class OrderSaga {
        async execute(userId, productId, quantity) {
          const compensations = [];
          
          try {
            // 1. 扣减库存
            await inventoryService.reduceStock(productId, quantity);
            compensations.push(() => inventoryService.restoreStock(productId, quantity));
            
            // 2. 创建订单
            const order = await orderService.create({ userId, productId, quantity });
            compensations.push(() => orderService.cancel(order.id));
            
            // 3. 扣款
            await paymentService.charge(userId, order.amount);
            compensations.push(() => paymentService.refund(userId, order.amount));
            
            return order;
          } catch (error) {
            // 失败了，执行补偿操作
            for (const compensate of compensations.reverse()) {
              await compensate();
            }
            throw error;
          }
        }
      }
      \`\`\`
      
      ### 2. 数据一致性
      
      **问题**：订单服务需要展示用户信息和商品信息，但它们在不同数据库。
      
      **解决**：CQRS + 事件溯源
      
      \`\`\`javascript
      // 订单服务维护自己的读模型
      class OrderReadModel {
        async onUserUpdated(event) {
          // 用户信息变化时，更新本地副本
          await this.db.query(
            'UPDATE order_user_cache SET name = ? WHERE user_id = ?',
            [event.data.name, event.data.userId]
          );
        }
        
        async onProductUpdated(event) {
          // 商品信息变化时，更新本地副本
          await this.db.query(
            'UPDATE order_product_cache SET price = ? WHERE product_id = ?',
            [event.data.price, event.data.productId]
          );
        }
        
        async getOrderDetail(orderId) {
          // 直接从本地读取，不用跨服务调用
          return this.db.query(\`
            SELECT o.*, u.name as user_name, p.name as product_name
            FROM orders o
            LEFT JOIN order_user_cache u ON o.user_id = u.user_id
            LEFT JOIN order_product_cache p ON o.product_id = p.product_id
            WHERE o.id = ?
          \`, [orderId]);
        }
      }
      \`\`\`
      
      ### 3. 服务雪崩
      
      **问题**：一个服务慢了，导致所有调用它的服务都变慢，最后全挂。
      
      **解决**：熔断器（Circuit Breaker）
      
      \`\`\`javascript
      const CircuitBreaker = require('opossum');
      
      // 配置熔断器
      const breaker = new CircuitBreaker(callUserService, {
        timeout: 3000, // 超时时间
        errorThresholdPercentage: 50, // 错误率超过50%触发熔断
        resetTimeout: 30000 // 30秒后尝试恢复
      });
      
      breaker.fallback(() => {
        // 熔断后的降级处理
        return { id: userId, name: '用户信息暂时不可用' };
      });
      
      // 使用
      const user = await breaker.fire(userId);
      \`\`\`
      
      ### 4. 调用链追踪
      
      **问题**：请求经过多个服务，出问题了不知道哪里慢。
      
      **解决**：用Jaeger做分布式追踪
      
      \`\`\`javascript
      const opentracing = require('opentracing');
      const jaeger = require('jaeger-client');
      
      // 初始化Jaeger
      const tracer = jaeger.initTracer({
        serviceName: 'order-service',
        sampler: {
          type: 'const',
          param: 1
        }
      });
      
      // 创建span
      app.use((req, res, next) => {
        const span = tracer.startSpan(req.path);
        
        res.on('finish', () => {
          span.setTag('http.status_code', res.statusCode);
          span.finish();
        });
        
        req.span = span;
        next();
      });
      \`\`\`
      
      ## 最终架构
      
      \`\`\`
                          ┌─────────────┐
                          │  API Gateway │
                          └──────┬──────┘
                                 │
               ┌─────────────────┼─────────────────┐
               │                 │                 │
          ┌────▼────┐       ┌────▼────┐      ┌────▼────┐
          │  User   │       │ Order   │      │ Product │
          │ Service │       │ Service │      │ Service │
          └────┬────┘       └────┬────┘      └────┬────┘
               │                 │                 │
          ┌────▼────┐       ┌────▼────┐      ┌────▼────┐
          │ MySQL   │       │ MySQL   │      │ MySQL   │
          └─────────┘       └─────────┘      └─────────┘
               │                 │                 │
               └─────────────────┼─────────────────┘
                                 │
                          ┌──────▼──────┐
                          │  RabbitMQ   │
                          └─────────────┘
      \`\`\`
      
      ## 收益与成本
      
      **收益**：
      - 部署时间：1小时 → 5分钟
      - 故障影响：全站 → 单个服务
      - 团队协作：经常冲突 → 独立开发
      - 扩容成本：整体扩容 → 按需扩容
      
      **成本**：
      - 服务器成本增加30%（多个服务）
      - 开发复杂度提升（分布式问题）
      - 运维难度增加（多个服务监控）
      - 改造周期长（6个月）
      
      ## 经验总结
      
      1. **不要为了微服务而微服务**：团队小、业务简单，单体应用足够了
      
      2. **拆分要循序渐进**：先拆最容易的，积累经验后再拆核心
      
      3. **做好监控和追踪**：微服务最怕的是出问题找不到原因
      
      4. **数据库也要拆分**：每个服务独立数据库，避免耦合
      
      5. **做好服务治理**：限流、熔断、降级一个都不能少
      
      微服务不是银弹，要结合实际情况。盲目跟风只会给自己挖坑。
      
      你们用微服务吗？遇到过什么问题？
      `
      },
      {
        title: 'TypeScript高级技巧：让你的代码更类型安全',
        slug: 'typescript-advanced-tips',
        excerpt: '不只是加个any，学会这些高级类型，TypeScript才算入门。',
        category: '编程语言',
        tags: ['TypeScript', '类型系统', '前端'],
        status: 'published',
        isFeatured: false,
        views: 2340,
        likes: 187,
        publishedAt: new Date('2025-09-28'),
        content: `
      很多人用TypeScript只会加个any，那还不如用JavaScript。今天分享一些实用的高级技巧。
      
      ## 1. 工具类型（Utility Types）
      
      ### Partial - 所有属性变可选
      
      \`\`\`typescript
      interface User {
        id: number;
        name: string;
        email: string;
        age: number;
      }
      
      // 更新用户时，不需要传所有字段
      function updateUser(id: number, data: Partial<User>) {
        // data 的所有属性都是可选的
      }
      
      updateUser(1, { name: 'New Name' }); // ✅
      updateUser(1, { age: 25 }); // ✅
      \`\`\`
      
      ### Required - 所有属性变必选
      
      \`\`\`typescript
      interface Config {
        host?: string;
        port?: number;
        timeout?: number;
      }
      
      // 运行时的配置必须是完整的
      function validateConfig(config: Required<Config>) {
        // 所有属性都必须提供
      }
      \`\`\`
      
      ### Pick - 选择部分属性
      
      \`\`\`typescript
      interface User {
        id: number;
        name: string;
        email: string;
        password: string;
        createdAt: Date;
      }
      
      // 只返回安全的用户信息
      type SafeUser = Pick<User, 'id' | 'name' | 'email'>;
      
      function getUserProfile(userId: number): SafeUser {
        // 不会返回 password 和 createdAt
        return {
          id: 1,
          name: 'John',
          email: 'john@example.com'
        };
      }
      \`\`\`
      
      ### Omit - 排除部分属性
      
      \`\`\`typescript
      // 创建用户时不需要 id
      type CreateUserDto = Omit<User, 'id' | 'createdAt'>;
      
      function createUser(data: CreateUserDto) {
        // data 没有 id 和 createdAt
      }
      \`\`\`
      
      ### Record - 创建对象类型
      
      \`\`\`typescript
      // 错误码映射
      type ErrorCode = 400 | 401 | 403 | 404 | 500;
      type ErrorMessages = Record<ErrorCode, string>;
      
      const errors: ErrorMessages = {
        400: 'Bad Request',
        401: 'Unauthorized',
        403: 'Forbidden',
        404: 'Not Found',
        500: 'Internal Server Error'
      };
      \`\`\`
      
      ## 2. 条件类型（Conditional Types）
      
      ### 基础用法
      
      \`\`\`typescript
      // T extends U ? X : Y
      type IsString<T> = T extends string ? true : false;
      
      type A = IsString<'hello'>; // true
      type B = IsString<123>; // false
      \`\`\`
      
      ### 实战：提取Promise返回值
      
      \`\`\`typescript
      type Unpromise<T> = T extends Promise<infer R> ? R : T;
      
      type A = Unpromise<Promise<string>>; // string
      type B = Unpromise<Promise<number>>; // number
      type C = Unpromise<boolean>; // boolean
      \`\`\`
      
      ### 实战：提取函数返回值
      
      \`\`\`typescript
      type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
      
      function getUser() {
        return { id: 1, name: 'John' };
      }
      
      type User = ReturnType<typeof getUser>; 
      // { id: number; name: string; }
      \`\`\`
      
      ## 3. 映射类型（Mapped Types）
      
      ### 自定义Readonly
      
      \`\`\`typescript
      type MyReadonly<T> = {
        readonly [P in keyof T]: T[P];
      };
      
      interface User {
        id: number;
        name: string;
      }
      
      type ReadonlyUser = MyReadonly<User>;
      // {
      //   readonly id: number;
      //   readonly name: string;
      // }
      \`\`\`
      
      ### 深度Readonly
      
      \`\`\`typescript
      type DeepReadonly<T> = {
        readonly [P in keyof T]: T[P] extends object 
          ? DeepReadonly<T[P]> 
          : T[P];
      };
      
      interface User {
        id: number;
        profile: {
          name: string;
          settings: {
            theme: string;
          };
        };
      }
      
      type ReadonlyUser = DeepReadonly<User>;
      // 所有嵌套属性都是 readonly
      \`\`\`
      
      ### 可空类型
      
      \`\`\`typescript
      type Nullable<T> = {
        [P in keyof T]: T[P] | null;
      };
      
      interface User {
        id: number;
        name: string;
      }
      
      type NullableUser = Nullable<User>;
      // {
      //   id: number | null;
      //   name: string | null;
      // }
      \`\`\`
      
      ## 4. 模板字符串类型
      
      \`\`\`typescript
      // 生成事件名称
      type EventName = \`on\${Capitalize<string>}\`;
      
      const onClick: EventName = 'onClick'; // ✅
      const onChange: EventName = 'onChange'; // ✅
      const click: EventName = 'click'; // ❌
      
      // API路径类型
      type APIPath = \`/api/\${string}\`;
      
      function callAPI(path: APIPath) {
        // path 必须以 /api/ 开头
      }
      
      callAPI('/api/users'); // ✅
      callAPI('/users'); // ❌
      
      // 组合使用
      type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
      type Endpoint = '/users' | '/products' | '/orders';
      type APIRequest = \`\${HTTPMethod} \${Endpoint}\`;
      
      const request: APIRequest = 'GET /users'; // ✅
      const request2: APIRequest = 'POST /products'; // ✅
      const request3: APIRequest = 'GET /invalid'; // ❌
      \`\`\`
      
      ## 5. 类型守卫（Type Guards）
      
      ### 自定义类型守卫
      
      \`\`\`typescript
      interface Cat {
        type: 'cat';
        meow(): void;
      }
      
      interface Dog {
        type: 'dog';
        bark(): void;
      }
      
      type Animal = Cat | Dog;
      
      // 类型守卫函数
      function isCat(animal: Animal): animal is Cat {
        return animal.type === 'cat';
      }
      
      function handleAnimal(animal: Animal) {
        if (isCat(animal)) {
          animal.meow(); // TypeScript 知道这是 Cat
        } else {
          animal.bark(); // TypeScript 知道这是 Dog
        }
      }
      \`\`\`
      
      ### 非空断言
      
      \`\`\`typescript
      function processUser(user: User | null) {
        // 方式1：类型守卫
        if (user !== null) {
          console.log(user.name);
        }
        
        // 方式2：非空断言（确定不为null时使用）
        console.log(user!.name);
      }
      \`\`\`
      
      ## 6. 泛型约束
      
      \`\`\`typescript
      // 基础泛型
      function identity<T>(arg: T): T {
        return arg;
      }
      
      // 泛型约束 - 必须有length属性
      function getLength<T extends { length: number }>(arg: T): number {
        return arg.length;
      }
      
      getLength('hello'); // ✅ string有length
      getLength([1, 2, 3]); // ✅ array有length
      getLength({ length: 10 }); // ✅ 对象有length
      getLength(123); // ❌ number没有length
      
      // 多个泛型参数
      function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
        return { ...obj1, ...obj2 };
      }
      
      const result = merge({ name: 'John' }, { age: 25 });
      // { name: string; age: number; }
      \`\`\`
      
      ## 7. 实战：API响应类型
      
      \`\`\`typescript
      // 统一的API响应格式
      interface ApiResponse<T> {
        code: number;
        message: string;
        data: T;
      }
      
      // 分页响应
      interface PaginatedResponse<T> {
        items: T[];
        total: number;
        page: number;
        pageSize: number;
      }
      
      // 用户相关API
      interface User {
        id: number;
        name: string;
        email: string;
      }
      
      // 获取用户列表
      async function getUsers(): Promise<ApiResponse<PaginatedResponse<User>>> {
        const res = await fetch('/api/users');
        return res.json();
      }
      
      // 使用时有完整的类型提示
      const response = await getUsers();
      console.log(response.data.items[0].name); // ✅ 类型安全
      \`\`\`
      
      ## 8. 实战：React组件Props
      
      \`\`\`typescript
      // 按钮组件Props
      interface ButtonProps {
        children: React.ReactNode;
        variant?: 'primary' | 'secondary' | 'danger';
        size?: 'sm' | 'md' | 'lg';
        disabled?: boolean;
        onClick?: () => void;
      }
      
      function Button({ 
        children, 
        variant = 'primary',
        size = 'md',
        ...props 
      }: ButtonProps) {
        return <button {...props}>{children}</button>;
      }
      
      // 继承原生属性
      interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
        variant?: 'primary' | 'secondary';
      }
      
      // 现在支持所有原生button属性
      <Button type="submit" disabled>Submit</Button>
      \`\`\`
      
      ## 9. 常见错误避免
      
      ### 错误1：滥用any
      
      \`\`\`typescript
      // ❌ 不好
      function process(data: any) {
        return data.value;
      }
      
      // ✅ 更好
      function process<T extends { value: unknown }>(data: T) {
        return data.value;
      }
      \`\`\`
      
      ### 错误2：类型断言滥用
      
      \`\`\`typescript
      // ❌ 危险
      const user = getUserData() as User;
      
      // ✅ 更安全
      function isUser(data: unknown): data is User {
        return typeof data === 'object' 
          && data !== null 
          && 'id' in data 
          && 'name' in data;
      }
      
      const data = getUserData();
      if (isUser(data)) {
        // 类型安全的使用
      }
      \`\`\`
      
      ### 错误3：忽略null/undefined
      
      \`\`\`typescript
      // tsconfig.json 启用严格模式
      {
        "compilerOptions": {
          "strictNullChecks": true
        }
      }
      
      // ❌ 可能运行时错误
      function getNameLength(user: User) {
        return user.name.length;
      }
      
      // ✅ 处理可能为空的情况
      function getNameLength(user: User | null) {
        return user?.name?.length ?? 0;
      }
      \`\`\`
      
      ## TypeScript配置推荐
      
      \`\`\`json
      {
        "compilerOptions": {
          "strict": true,
          "strictNullChecks": true,
          "strictFunctionTypes": true,
          "noImplicitAny": true,
          "noUnusedLocals": true,
          "noUnusedParameters": true,
          "noImplicitReturns": true,
          "noFallthroughCasesInSwitch": true
        }
      }
      \`\`\`
      
      TypeScript的类型系统非常强大，学会这些高级技巧，可以大大提升代码质量和开发效率。
      
      你最常用的TypeScript技巧是什么？
      `
      },{
        title: '记一次Node.js内存泄漏排查：从16GB到200MB',
        slug: 'nodejs-memory-leak-debugging',
        excerpt: '线上服务器内存持续飙升，最后OOM崩溃。用了3天时间定位并解决了这个隐藏的内存泄漏。',
        category: '问题排查',
        tags: ['Node.js', '内存泄漏', '性能优化'],
        status: 'published',
        isFeatured: true,
        views: 3890,
        likes: 312,
        publishedAt: new Date('2025-09-25'),
        content: `
      周一早上刚到公司，就收到告警：生产环境Node.js服务OOM了，进程被kill。这已经是本周第三次了。
      
      ## 问题现象
      
      监控显示：
      - 服务启动时内存200MB
      - 运行12小时后涨到8GB
      - 24小时后达到16GB，触发OOM
      - 重启后循环往复
      
      用户反馈：下午开始接口变慢，晚上直接超时。
      
      ## 排查工具准备
      
      ### 1. 安装必要工具
      
      \`\`\`bash
      # heapdump - 生成堆快照
      npm install heapdump --save-dev
      
      # clinic - 性能分析工具
      npm install -g clinic
      
      # node-memwatch - 内存监控
      npm install memwatch-next --save-dev
      \`\`\`
      
      ### 2. 开启GC日志
      
      \`\`\`bash
      # 启动时加上GC日志参数
      node --trace-gc --trace-gc-verbose app.js
      \`\`\`
      
      ## 定位问题
      
      ### 第一步：对比堆快照
      
      在代码中添加手动生成快照的接口：
      
      \`\`\`javascript
      const heapdump = require('heapdump');
      const express = require('express');
      const app = express();
      
      // 添加生成堆快照的路由
      app.get('/heapdump', (req, res) => {
        const filename = \`heapdump-\${Date.now()}.heapsnapshot\`;
        heapdump.writeSnapshot(filename, (err, filepath) => {
          if (err) {
            res.status(500).send('Failed to generate heap snapshot');
          } else {
            res.send(\`Heap snapshot written to \${filepath}\`);
          }
        });
      });
      \`\`\`
      
      分别在启动时、运行6小时、12小时后生成快照：
      
      \`\`\`bash
      curl http://localhost:3000/heapdump
      \`\`\`
      
      ### 第二步：Chrome DevTools分析
      
      1. 打开Chrome，按F12，切换到Memory标签
      2. 点击"Load"加载文件
      3. 切换到"Comparison"视图，对比两个快照
      
      **发现问题**：
      - Array对象增加了30万个
      - 占用内存8GB
      - 来自 EventEmitter 的监听器
      
      ### 第三步：代码审查
      
      搜索项目中的 EventEmitter 使用：
      
      \`\`\`javascript
      // ❌ 问题代码
      class DataProcessor {
        constructor(db) {
          this.db = db;
          this.results = [];
          
          // 每次处理都添加监听器，但从不移除！
          this.db.on('data', (data) => {
            this.results.push(data);
          });
        }
        
        async process(query) {
          return this.db.query(query);
        }
      }
      
      // 业务代码中每次请求都创建新实例
      app.get('/api/data', async (req, res) => {
        const processor = new DataProcessor(db);
        const result = await processor.process(req.query);
        res.json(result);
      });
      \`\`\`
      
      **问题分析**：
      1. 每个请求创建一个 DataProcessor 实例
      2. 每个实例都给 db 添加监听器
      3. 但监听器从未移除
      4. 一天10万个请求 = 10万个监听器 = 内存爆炸
      
      ## 解决方案
      
      ### 方案1：移除监听器（推荐）
      
      \`\`\`javascript
      // ✅ 正确做法
      class DataProcessor {
        constructor(db) {
          this.db = db;
          this.results = [];
          
          // 绑定到实例，方便后续移除
          this.handleData = this.handleData.bind(this);
          this.db.on('data', this.handleData);
        }
        
        handleData(data) {
          this.results.push(data);
        }
        
        async process(query) {
          try {
            return await this.db.query(query);
          } finally {
            // 处理完后移除监听器
            this.db.removeListener('data', this.handleData);
          }
        }
        
        // 或者提供显式的清理方法
        destroy() {
          this.db.removeListener('data', this.handleData);
          this.results = null;
        }
      }
      
      // 使用完记得清理
      app.get('/api/data', async (req, res) => {
        const processor = new DataProcessor(db);
        try {
          const result = await processor.process(req.query);
          res.json(result);
        } finally {
          processor.destroy();
        }
      });
      \`\`\`
      
      ### 方案2：使用once代替on
      
      如果只需要监听一次：
      
      \`\`\`javascript
      // ✅ 自动移除
      this.db.once('data', (data) => {
        this.results.push(data);
      });
      \`\`\`
      
      ### 方案3：单例模式
      
      \`\`\`javascript
      // ✅ 全局只创建一个实例
      class DataProcessor {
        private static instance: DataProcessor;
        
        private constructor(db) {
          this.db = db;
          this.db.on('data', this.handleData);
        }
        
        static getInstance(db) {
          if (!DataProcessor.instance) {
            DataProcessor.instance = new DataProcessor(db);
          }
          return DataProcessor.instance;
        }
      }
      
      // 使用单例
      const processor = DataProcessor.getInstance(db);
      \`\`\`
      
      ## 验证修复
      
      ### 1. 本地压测
      
      \`\`\`bash
      # 使用autocannon压测
      npx autocannon -c 100 -d 60 http://localhost:3000/api/data
      \`\`\`
      
      修复前：内存从200MB涨到2GB  
      修复后：内存稳定在250MB
      
      ### 2. 监听器数量检查
      
      添加监控代码：
      
      \`\`\`javascript
      const EventEmitter = require('events');
      
      // 监控监听器数量
      setInterval(() => {
        const emitters = [db, app, server];
        emitters.forEach(emitter => {
          if (emitter instanceof EventEmitter) {
            const events = emitter.eventNames();
            events.forEach(event => {
              const count = emitter.listenerCount(event);
              if (count > 10) {
                console.warn(\`⚠️ Event "\${event}" has \${count} listeners!\`);
              }
            });
          }
        });
      }, 60000); // 每分钟检查一次
      \`\`\`
      
      ### 3. 设置最大监听器数量
      
      防止类似问题：
      
      \`\`\`javascript
      // 超过这个数量会告警
      db.setMaxListeners(20);
      \`\`\`
      
      ## 其他常见内存泄漏场景
      
      ### 1. 全局变量累积
      
      \`\`\`javascript
      // ❌ 问题代码
      let cache = {};
      
      app.get('/api/users/:id', (req, res) => {
        // 缓存持续增长，永不清理
        cache[req.params.id] = fetchUser(req.params.id);
        res.json(cache[req.params.id]);
      });
      
      // ✅ 使用LRU缓存
      const LRU = require('lru-cache');
      const cache = new LRU({ 
        max: 500,
        maxAge: 1000 * 60 * 60 // 1小时
      });
      \`\`\`
      
      ### 2. 闭包引用
      
      \`\`\`javascript
      // ❌ 问题代码
      function processData(largeData) {
        return function() {
          // 即使不用largeData，也会被闭包持有
          console.log('Processing complete');
        };
      }
      
      // ✅ 手动释放
      function processData(largeData) {
        // 处理完后
        const result = doSomething(largeData);
        largeData = null; // 显式释放
        
        return function() {
          console.log('Processing complete');
        };
      }
      \`\`\`
      
      ### 3. 定时器未清理
      
      \`\`\`javascript
      // ❌ 问题代码
      class UserSession {
        constructor(user) {
          this.user = user;
          // 定时器持有this引用
          this.timer = setInterval(() => {
            this.heartbeat();
          }, 5000);
        }
      }
      
      // ✅ 清理定时器
      class UserSession {
        destroy() {
          if (this.timer) {
            clearInterval(this.timer);
            this.timer = null;
          }
          this.user = null;
        }
      }
      \`\`\`
      
      ### 4. Stream未关闭
      
      \`\`\`javascript
      // ❌ 问题代码
      app.get('/download', (req, res) => {
        const stream = fs.createReadStream('large-file.zip');
        stream.pipe(res);
        // 如果客户端断开，stream可能不会自动关闭
      });
      
      // ✅ 正确处理
      app.get('/download', (req, res) => {
        const stream = fs.createReadStream('large-file.zip');
        
        stream.on('error', (err) => {
          console.error(err);
          stream.destroy();
        });
        
        res.on('close', () => {
          stream.destroy();
        });
        
        stream.pipe(res);
      });
      \`\`\`
      
      ## 预防措施
      
      ### 1. 代码审查清单
      
      - [ ] EventEmitter的on有对应的removeListener
      - [ ] setInterval/setTimeout有对应的clear
      - [ ] Stream有正确的错误处理和关闭
      - [ ] 大对象使用完后置为null
      - [ ] 缓存有过期机制
      - [ ] 避免意外的全局变量
      
      ### 2. 自动化检测
      
      在CI中添加内存泄漏检测：
      
      \`\`\`javascript
      // test/memory-leak.test.js
      const { spawn } = require('child_process');
      
      describe('Memory Leak Test', () => {
        it('should not leak memory under load', (done) => {
          const server = spawn('node', ['--expose-gc', 'app.js']);
          
          let initialMemory;
          let finalMemory;
          
          // 压测前记录内存
          setTimeout(() => {
            initialMemory = process.memoryUsage().heapUsed;
            
            // 运行压测
            runLoadTest();
            
            // 压测后记录内存
            setTimeout(() => {
              global.gc(); // 手动GC
              finalMemory = process.memoryUsage().heapUsed;
              
              // 内存增长不应超过50%
              const growth = (finalMemory - initialMemory) / initialMemory;
              expect(growth).toBeLessThan(0.5);
              
              server.kill();
              done();
            }, 60000);
          }, 5000);
        });
      });
      \`\`\`
      
      ### 3. 生产环境监控
      
      \`\`\`javascript
      const v8 = require('v8');
      const process = require('process');
      
      // 定期上报内存指标
      setInterval(() => {
        const usage = process.memoryUsage();
        const heapStats = v8.getHeapStatistics();
        
        metrics.gauge('memory.rss', usage.rss);
        metrics.gauge('memory.heapUsed', usage.heapUsed);
        metrics.gauge('memory.heapTotal', heapStats.total_heap_size);
        metrics.gauge('memory.heapLimit', heapStats.heap_size_limit);
        
        // 内存使用率超过80%告警
        const usagePercent = heapStats.used_heap_size / heapStats.heap_size_limit;
        if (usagePercent > 0.8) {
          alert('High memory usage: ' + (usagePercent * 100).toFixed(2) + '%');
        }
      }, 60000);
      \`\`\`
      
      ## 最终效果
      
      修复部署后：
      - 内存稳定在200-300MB之间
      - 运行7天无OOM
      - 接口响应时间稳定
      - 服务器成本降低60%（不用频繁重启和扩容）
      
      ## 工具推荐
      
      1. **heapdump** - 生成堆快照
      2. **clinic** - 全方位性能分析
      3. **memwatch-next** - 实时内存监控
      4. **node-inspect** - Chrome DevTools远程调试
      5. **0x** - 火焰图生成
      
      内存泄漏是Node.js生产环境最常见的问题之一。掌握这些排查技巧，能帮你快速定位和解决问题。
      
      记住：**任何监听器、定时器、Stream都要配对清理！**
      
      你遇到过哪些内存泄漏的坑？
      `
      },
      {
        title: 'React性能优化实战：解决3秒白屏和卡顿问题',
        slug: 'react-performance-optimization-complete-guide',
        excerpt: '接手的老项目首屏白屏3秒，列表滚动卡顿。用这些方法优化后，性能提升10倍。',
        category: '性能优化',
        tags: ['React', '性能优化', '前端'],
        status: 'published',
        isFeatured: false,
        views: 2560,
        likes: 198,
        publishedAt: new Date('2025-09-22'),
        content: `
      项目是个B端管理系统，1000+个组件，用户抱怨"卡得像PPT"。Chrome DevTools一看，主线程阻塞严重。
      
      ## 问题诊断
      
      ### 使用React DevTools Profiler
      
      打开React DevTools，切换到Profiler标签，录制一次操作：
      
      **发现问题**：
      1. 首次渲染耗时3200ms
      2. 单次setState触发800个组件重渲染
      3. 某个列表组件渲染1次要500ms
      
      ## 优化方案
      
      ### 1. 组件拆分和懒加载
      
      **问题代码**：
      
      \`\`\`jsx
      // App.js - 一个文件2000行
      import Dashboard from './Dashboard';
      import UserManagement from './UserManagement';
      import ProductList from './ProductList';
      import OrderList from './OrderList';
      // ... 还有30个组件
      
      function App() {
        return (
          <Router>
            <Layout>
              <Routes>
                <Route path="/dashboard" element={<Dashboard />} />
                <Route path="/users" element={<UserManagement />} />
                <Route path="/products" element={<ProductList />} />
                <Route path="/orders" element={<OrderList />} />
                {/* ... 30个路由 */}
              </Routes>
            </Layout>
          </Router>
        );
      }
      \`\`\`
      
      **优化后**：
      
      \`\`\`jsx
      import { lazy, Suspense } from 'react';
      import { Skeleton } from 'antd';
      
      // 路由级懒加载
      const Dashboard = lazy(() => import('./pages/Dashboard'));
      const UserManagement = lazy(() => import('./pages/UserManagement'));
      const ProductList = lazy(() => import('./pages/ProductList'));
      
      // 加载组件
      const PageLoader = () => (
        <div style={{ padding: 24 }}>
          <Skeleton active paragraph={{ rows: 8 }} />
        </div>
      );
      
      function App() {
        return (
          <Router>
            <Layout>
              <Suspense fallback={<PageLoader />}>
                <Routes>
                  <Route path="/dashboard" element={<Dashboard />} />
                  <Route path="/users" element={<UserManagement />} />
                  <Route path="/products" element={<ProductList />} />
                </Routes>
              </Suspense>
            </Layout>
          </Router>
        );
      }
      \`\`\`
      
      **效果**：首屏bundle从2.5MB降到600KB，加载时间从3.2s降到0.8s。
      
      ### 2. 使用React.memo避免无效渲染
      
      **问题代码**：
      
      \`\`\`jsx
      // 父组件每次setState，所有子组件都重渲染
      function UserList({ users }) {
        const [search, setSearch] = useState('');
        
        return (
          <>
            <SearchBar value={search} onChange={setSearch} />
            {users.map(user => (
              <UserCard key={user.id} user={user} />
            ))}
          </>
        );
      }
      
      function UserCard({ user }) {
        console.log('UserCard渲染:', user.id);
        return <div>{user.name}</div>;
      }
      \`\`\`
      
      输入搜索框时，所有UserCard都会重渲染，即使数据没变。
      
      **优化后**：
      
      \`\`\`jsx
      // 用React.memo包裹
      const UserCard = React.memo(({ user, onEdit }) => {
        console.log('UserCard渲染:', user.id);
        return (
          <div>
            {user.name}
            <button onClick={() => onEdit(user.id)}>编辑</button>
          </div>
        );
      }, (prevProps, nextProps) => {
        // 自定义比较函数，只有user真正改变时才重渲染
        return prevProps.user.id === nextProps.user.id &&
               prevProps.user.name === nextProps.user.name;
      });
      
      // 使用useCallback避免函数引用变化
      function UserList({ users }) {
        const [search, setSearch] = useState('');
        
        const handleEdit = useCallback((userId) => {
          // 编辑逻辑
        }, []); // 空依赖，函数引用不变
        
        return (
          <>
            <SearchBar value={search} onChange={setSearch} />
            {users.map(user => (
              <UserCard 
                key={user.id} 
                user={user}
                onEdit={handleEdit}
              />
            ))}
          </>
        );
      }
      \`\`\`
      
      **效果**：输入搜索时，只有SearchBar重渲染，用户卡片不再闪烁。
      
      ### 3. useMemo缓存计算结果
      
      **问题代码**：
      
      \`\`\`jsx
      function OrderSummary({ orders }) {
        // 每次渲染都重新计算，即使orders没变
        const total = orders.reduce((sum, order) => sum + order.amount, 0);
        const average = total / orders.length;
        const max = Math.max(...orders.map(o => o.amount));
        
        return (
          <div>
            <p>总金额: {total}</p>
            <p>平均: {average}</p>
            <p>最大: {max}</p>
          </div>
        );
      }
      \`\`\`
      
      **优化后**：
      
      \`\`\`jsx
      function OrderSummary({ orders }) {
        // 只有orders变化时才重新计算
        const statistics = useMemo(() => {
          const total = orders.reduce((sum, order) => sum + order.amount, 0);
          const average = total / orders.length;
          const max = Math.max(...orders.map(o => o.amount));
          
          return { total, average, max };
        }, [orders]);
        
        return (
          <div>
            <p>总金额: {statistics.total}</p>
            <p>平均: {statistics.average}</p>
            <p>最大: {statistics.max}</p>
          </div>
        );
      }
      \`\`\`
      
      ### 4. 虚拟滚动处理长列表
      
      **问题代码**：
      
      \`\`\`jsx
      // 直接渲染10000条数据，页面卡死
      function ProductList({ products }) {
        return (
          <div>
            {products.map(product => (
              <ProductCard key={product.id} product={product} />
            ))}
          </div>
        );
      }
      \`\`\`
      
      **优化后**：
      
      \`\`\`jsx
      import { FixedSizeList } from 'react-window';
      
      function ProductList({ products }) {
        const Row = ({ index, style }) => (
          <div style={style}>
            <ProductCard product={products[index]} />
          </div>
        );
        
        return (
          <FixedSizeList
            height={600}
            itemCount={products.length}
            itemSize={100}
            width="100%"
          >
            {Row}
          </FixedSizeList>
        );
      }
      \`\`\`
      
      **效果**：10000条数据，只渲染可见的20条，滚动流畅。
      
      ### 5. 状态管理优化
      
      **问题代码**：
      
      \`\`\`jsx
      // 整个应用一个大state，任何改动都触发全局更新
      function App() {
        const [state, setState] = useState({
          users: [],
          products: [],
          orders: [],
          ui: {
            sidebar: true,
            theme: 'light'
          }
        });
        
        // 改个侧边栏状态，整个应用重渲染
        const toggleSidebar = () => {
          setState({
            ...state,
            ui: { ...state.ui, sidebar: !state.ui.sidebar }
          });
        };
        
        return (
          <AppContext.Provider value={state}>
            <Layout />
          </AppContext.Provider>
        );
      }
      \`\`\`
      
      **优化后**：
      
      \`\`\`jsx
      // 1. 拆分Context
      const UserContext = createContext();
      const ProductContext = createContext();
      const UIContext = createContext();
      
      function App() {
        const [users, setUsers] = useState([]);
        const [products, setProducts] = useState([]);
        const [ui, setUI] = useState({ sidebar: true, theme: 'light' });
        
        return (
          <UserContext.Provider value={{ users, setUsers }}>
            <ProductContext.Provider value={{ products, setProducts }}>
              <UIContext.Provider value={{ ui, setUI }}>
                <Layout />
              </UIContext.Provider>
            </ProductContext.Provider>
          </UserContext.Provider>
        );
      }
      
      // 2. 或使用Zustand
      import create from 'zustand';
      
      // 按模块拆分store
      const useUserStore = create((set) => ({
        users: [],
        setUsers: (users) => set({ users })
      }));
      
      const useUIStore = create((set) => ({
        sidebar: true,
        theme: 'light',
        toggleSidebar: () => set(state => ({ sidebar: !state.sidebar }))
      }));
      
      // 组件只订阅需要的状态
      function UserList() {
        const users = useUserStore(state => state.users);
        // UI变化不会触发这个组件重渲染
        return <div>{/* ... */}</div>;
      }
      \`\`\`
      
      ### 6. 防抖和节流
      
      **问题代码**：
      
      \`\`\`jsx
      // 搜索框每输入一个字就发请求
      function SearchBar() {
        const [keyword, setKeyword] = useState('');
        
        const handleChange = (e) => {
          setKeyword(e.target.value);
          fetch(\`/api/search?q=\${e.target.value}\`); // 疯狂发请求
        };
        
        return <input onChange={handleChange} />;
      }
      \`\`\`
      
      **优化后**：
      
      \`\`\`jsx
      import { useMemo } from 'react';
      import debounce from 'lodash/debounce';
      
      function SearchBar() {
        const [keyword, setKeyword] = useState('');
        
        // 防抖：停止输入500ms后才搜索
        const debouncedSearch = useMemo(
          () => debounce((value) => {
            fetch(\`/api/search?q=\${value}\`);
          }, 500),
          []
        );
        
        const handleChange = (e) => {
          const value = e.target.value;
          setKeyword(value);
          debouncedSearch(value);
        };
        
        // 组件卸载时取消防抖
        useEffect(() => {
          return () => {
            debouncedSearch.cancel();
          };
        }, [debouncedSearch]);
        
        return <input value={keyword} onChange={handleChange} />;
      }
      \`\`\`
      
      ### 7. 图片懒加载
      
      \`\`\`jsx
      import { LazyLoadImage } from 'react-lazy-load-image-component';
      import 'react-lazy-load-image-component/src/effects/blur.css';
      
      function ProductCard({ product }) {
        return (
          <div>
            <LazyLoadImage
              src={product.image}
              alt={product.name}
              effect="blur"
              width={300}
              height={200}
              // 提前加载可视区域上方500px的图片
              threshold={500}
            />
            <h3>{product.name}</h3>
          </div>
        );
      }
      \`\`\`
      
      ### 8. 错误边界避免整个应用崩溃
      
      \`\`\`jsx
      class ErrorBoundary extends React.Component {
        state = { hasError: false, error: null };
        
        static getDerivedStateFromError(error) {
          return { hasError: true, error };
        }
        
        componentDidCatch(error, errorInfo) {
          // 上报错误
          console.error('Error caught:', error, errorInfo);
        }
        
        render() {
          if (this.state.hasError) {
            return (
              <div style={{ padding: 20 }}>
                <h2>出错了</h2>
                <button onClick={() => this.setState({ hasError: false })}>
                  重试
                </button>
              </div>
            );
          }
          
          return this.props.children;
        }
      }
      
      // 使用
      <ErrorBoundary>
        <ProductList />
      </ErrorBoundary>
      \`\`\`
      
      ## 性能监控
      
      \`\`\`jsx
      // 添加性能监控
      import { useEffect } from 'react';
      
      function usePerformanceMonitor(componentName) {
        useEffect(() => {
          const startTime = performance.now();
          
          return () => {
            const endTime = performance.now();
            const renderTime = endTime - startTime;
            
            if (renderTime > 16) { // 超过一帧(16ms)就告警
              console.warn(\`⚠️ \${componentName} 渲染耗时: \${renderTime.toFixed(2)}ms\`);
            }
          };
        });
      }
      
      function ExpensiveComponent() {
        usePerformanceMonitor('ExpensiveComponent');
        
        return <div>{/* ... */}</div>;
      }
      \`\`\`
      
      ## 性能检查清单
      
      上线前过一遍：
      
      - [ ] 路由级代码分割
      - [ ] 大组件拆分为小组件
      - [ ] 列表项使用React.memo
      - [ ] 事件处理函数使用useCallback
      - [ ] 复杂计算使用useMemo
      - [ ] 长列表使用虚拟滚动
      - [ ] 图片懒加载
      - [ ] 接口请求防抖/节流
      - [ ] 避免内联对象/数组
      - [ ] Context按需拆分
      - [ ] 错误边界防止崩溃
      
      ## 最终效果
      
      优化后的指标：
      
      | 指标 | 优化前 | 优化后 | 提升 |
      |------|--------|--------|------|
      | 首屏加载 | 3.2s | 0.8s | 4倍 |
      | FPS | 15-20 | 55-60 | 3倍 |
      | 列表渲染 | 500ms | 50ms | 10倍 |
      | Bundle大小 | 2.5MB | 600KB | 4倍 |
      
      用户反馈："终于不卡了！"
      
      ## 常见误区
      
      1. **过度优化**：不要一开始就优化，先profiling找瓶颈
      2. **滥用memo**：简单组件memo反而更慢
      3. **过度拆分Context**：拆分过细维护成本高
      4. **忽略网络优化**：再快的渲染也等不起慢接口
      
      记住：**先测量，再优化。不要凭感觉。**
      
      你的React应用有性能问题吗？用这些方法试试！
      `
      },
      {
        title: 'JWT实践：Token续期和安全问题处理',
        slug: 'jwt-authentication-best-practices',
        excerpt: 'JWT用了挺久，分享下实际项目中遇到的token续期、安全存储这些问题的处理方法。',
        category: '后端技术',
        tags: ['JWT', '认证', '安全'],
        status: 'published',
        isFeatured: false,
        views: 1890,
        likes: 145,
        publishedAt: new Date('2025-09-20'),
        content: `
      项目里一直用JWT做认证，用着用着发现有些问题需要处理，记录一下。
      
      ## 基础实现
      
      最开始的实现很简单：
      
      \`\`\`javascript
      const jwt = require('jsonwebtoken');
      
      // 登录生成token
      app.post('/api/login', async (req, res) => {
        const { email, password } = req.body;
        
        const user = await User.findOne({ email });
        if (!user || !await bcrypt.compare(password, user.password)) {
          return res.status(401).json({ error: '用户名或密码错误' });
        }
        
        const token = jwt.sign(
          { userId: user.id, email: user.email },
          process.env.JWT_SECRET,
          { expiresIn: '7d' }
        );
        
        res.json({ token, user });
      });
      
      // 验证中间件
      const authMiddleware = (req, res, next) => {
        const token = req.headers.authorization?.replace('Bearer ', '');
        
        if (!token) {
          return res.status(401).json({ error: '未登录' });
        }
        
        try {
          const decoded = jwt.verify(token, process.env.JWT_SECRET);
          req.user = decoded;
          next();
        } catch (error) {
          res.status(401).json({ error: 'Token无效' });
        }
      };
      \`\`\`
      
      这套用了一段时间，后来发现几个问题。
      
      ## 问题1：Token过期后的处理
      
      设置7天过期，用户正在操作突然过期了，体验不好。
      
      常见的解决方案是双token：
      
      \`\`\`javascript
      // 生成访问token和刷新token
      function generateTokens(user) {
        const accessToken = jwt.sign(
          { userId: user.id },
          process.env.JWT_SECRET,
          { expiresIn: '15m' }  // 访问token 15分钟
        );
        
        const refreshToken = jwt.sign(
          { userId: user.id, type: 'refresh' },
          process.env.REFRESH_SECRET,
          { expiresIn: '7d' }  // 刷新token 7天
        );
        
        return { accessToken, refreshToken };
      }
      
      // 登录
      app.post('/api/login', async (req, res) => {
        // ... 验证用户
        
        const tokens = generateTokens(user);
        
        // 刷新token存到数据库
        await RefreshToken.create({
          userId: user.id,
          token: tokens.refreshToken,
          expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
        });
        
        res.json({ ...tokens, user });
      });
      
      // 刷新token
      app.post('/api/refresh', async (req, res) => {
        const { refreshToken } = req.body;
        
        try {
          const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
          
          // 检查token是否在数据库中
          const stored = await RefreshToken.findOne({
            userId: decoded.userId,
            token: refreshToken
          });
          
          if (!stored) {
            return res.status(401).json({ error: '无效的刷新token' });
          }
          
          const user = await User.findById(decoded.userId);
          const newTokens = generateTokens(user);
          
          // 删除旧的刷新token
          await RefreshToken.deleteOne({ token: refreshToken });
          
          // 保存新的刷新token
          await RefreshToken.create({
            userId: user.id,
            token: newTokens.refreshToken,
            expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
          });
          
          res.json(newTokens);
        } catch (error) {
          res.status(401).json({ error: '刷新失败' });
        }
      });
      \`\`\`
      
      前端配合axios拦截器：
      
      \`\`\`javascript
      let isRefreshing = false;
      let failedQueue = [];
      
      const processQueue = (error, token = null) => {
        failedQueue.forEach(prom => {
          if (error) {
            prom.reject(error);
          } else {
            prom.resolve(token);
          }
        });
        failedQueue = [];
      };
      
      axios.interceptors.response.use(
        response => response,
        async error => {
          const originalRequest = error.config;
          
          if (error.response?.status === 401 && !originalRequest._retry) {
            if (isRefreshing) {
              // 正在刷新，把请求加入队列
              return new Promise((resolve, reject) => {
                failedQueue.push({ resolve, reject });
              }).then(token => {
                originalRequest.headers['Authorization'] = 'Bearer ' + token;
                return axios(originalRequest);
              });
            }
            
            originalRequest._retry = true;
            isRefreshing = true;
            
            const refreshToken = localStorage.getItem('refreshToken');
            
            try {
              const { data } = await axios.post('/api/refresh', { refreshToken });
              
              localStorage.setItem('accessToken', data.accessToken);
              localStorage.setItem('refreshToken', data.refreshToken);
              
              axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.accessToken;
              originalRequest.headers['Authorization'] = 'Bearer ' + data.accessToken;
              
              processQueue(null, data.accessToken);
              
              return axios(originalRequest);
            } catch (err) {
              processQueue(err, null);
              // 刷新失败，跳转到登录页
              window.location.href = '/login';
              return Promise.reject(err);
            } finally {
              isRefreshing = false;
            }
          }
          
          return Promise.reject(error);
        }
      );
      \`\`\`
      
      ## 问题2：Token存储位置
      
      之前都存localStorage，但有XSS风险。
      
      几种方案对比：
      
      **localStorage**
      - 优点：简单、不会过期
      - 缺点：容易被XSS攻击窃取
      
      **Cookie (httpOnly)**
      - 优点：httpOnly防XSS、secure防中间人
      - 缺点：容易CSRF、跨域麻烦
      
      **内存存储**
      - 优点：最安全
      - 缺点：刷新页面就丢了
      
      我现在的方案是：accessToken放内存，refreshToken放httpOnly cookie
      
      \`\`\`javascript
      // 后端设置cookie
      app.post('/api/login', async (req, res) => {
        // ... 验证用户
        
        const tokens = generateTokens(user);
        
        // refreshToken放cookie
        res.cookie('refreshToken', tokens.refreshToken, {
          httpOnly: true,
          secure: process.env.NODE_ENV === 'production',
          sameSite: 'strict',
          maxAge: 7 * 24 * 60 * 60 * 1000
        });
        
        // accessToken返回给前端
        res.json({ 
          accessToken: tokens.accessToken,
          user 
        });
      });
      
      // 刷新接口从cookie读取
      app.post('/api/refresh', async (req, res) => {
        const refreshToken = req.cookies.refreshToken;
        
        if (!refreshToken) {
          return res.status(401).json({ error: '未登录' });
        }
        
        // ... 刷新逻辑
      });
      \`\`\`
      
      前端只保存accessToken在内存：
      
      \`\`\`javascript
      // 不用localStorage了
      let accessToken = null;
      
      export const setAccessToken = (token) => {
        accessToken = token;
      };
      
      export const getAccessToken = () => {
        return accessToken;
      };
      
      // axios拦截器
      axios.interceptors.request.use(config => {
        const token = getAccessToken();
        if (token) {
          config.headers.Authorization = \`Bearer \${token}\`;
        }
        return config;
      });
      \`\`\`
      
      ## 问题3：退出登录的处理
      
      JWT是无状态的，没法让它立即失效。只能等它自然过期。
      
      有几种办法：
      
      **方案1：黑名单（用Redis）**
      
      \`\`\`javascript
      // 退出登录
      app.post('/api/logout', authMiddleware, async (req, res) => {
        const token = req.headers.authorization.replace('Bearer ', '');
        
        // 解析token获取过期时间
        const decoded = jwt.decode(token);
        const expiresIn = decoded.exp - Math.floor(Date.now() / 1000);
        
        // 加入黑名单，过期时间和token一致
        await redis.setex(\`blacklist:\${token}\`, expiresIn, '1');
        
        // 删除刷新token
        await RefreshToken.deleteOne({ userId: req.user.userId });
        
        res.json({ message: '已退出' });
      });
      
      // 验证时检查黑名单
      const authMiddleware = async (req, res, next) => {
        const token = req.headers.authorization?.replace('Bearer ', '');
        
        if (!token) {
          return res.status(401).json({ error: '未登录' });
        }
        
        // 检查是否在黑名单
        const inBlacklist = await redis.exists(\`blacklist:\${token}\`);
        if (inBlacklist) {
          return res.status(401).json({ error: 'Token已失效' });
        }
        
        try {
          const decoded = jwt.verify(token, process.env.JWT_SECRET);
          req.user = decoded;
          next();
        } catch (error) {
          res.status(401).json({ error: 'Token无效' });
        }
      };
      \`\`\`
      
      **方案2：白名单**
      
      只把有效的token存Redis，每次验证都查一下。但这样就失去了JWT无状态的优势。
      
      **方案3：短过期+频繁刷新**
      
      accessToken设置很短（5-15分钟），即使泄露了影响也小。
      
      我现在用的是方案1的黑名单，退出登录比较少，对性能影响不大。
      
      ## 问题4：权限验证
      
      除了登录认证，还需要权限验证：
      
      \`\`\`javascript
      // token里包含角色
      function generateTokens(user) {
        const accessToken = jwt.sign(
          { 
            userId: user.id,
            role: user.role  // admin, user, guest
          },
          process.env.JWT_SECRET,
          { expiresIn: '15m' }
        );
        // ...
      }
      
      // 权限中间件
      const requireRole = (...roles) => {
        return (req, res, next) => {
          if (!req.user) {
            return res.status(401).json({ error: '未登录' });
          }
          
          if (!roles.includes(req.user.role)) {
            return res.status(403).json({ error: '权限不足' });
          }
          
          next();
        };
      };
      
      // 使用
      app.delete('/api/users/:id', 
        authMiddleware,
        requireRole('admin', 'super_admin'),
        async (req, res) => {
          // 只有管理员能删除用户
        }
      );
      \`\`\`
      
      ## 其他注意事项
      
      **1. Secret要够长**
      
      \`\`\`bash
      # 生成随机secret
      openssl rand -hex 64
      \`\`\`
      
      **2. 不要在token里放敏感信息**
      
      JWT的payload是base64编码，不是加密，任何人都能解码看到。
      
      \`\`\`javascript
      // ❌ 不要这样
      jwt.sign({ 
        userId: user.id,
        password: user.password,  // 千万别放密码
        idCard: user.idCard       // 别放身份证
      }, secret);
      
      // ✅ 只放必要信息
      jwt.sign({ 
        userId: user.id,
        role: user.role
      }, secret);
      \`\`\`
      
      **3. 设置合理的过期时间**
      
      - accessToken：15分钟到1小时
      - refreshToken：7天到30天
      
      **4. 多设备登录的处理**
      
      如果只允许一个设备登录，可以在数据库记录最新的refreshToken，登录时让旧的失效。
      
      \`\`\`javascript
      // 登录时
      app.post('/api/login', async (req, res) => {
        // ... 验证用户
        
        // 删除该用户的所有旧token
        await RefreshToken.deleteMany({ userId: user.id });
        
        // 生成新token
        const tokens = generateTokens(user);
        
        // 保存新token
        await RefreshToken.create({
          userId: user.id,
          token: tokens.refreshToken
        });
        
        res.json({ ...tokens, user });
      });
      \`\`\`
      
      ## 总结
      
      JWT用起来挺方便，但细节挺多：
      - 双token解决过期问题
      - 存储位置要考虑安全性
      - 退出登录用黑名单
      - 权限验证别忘了
      - payload不要放敏感信息
      
      这些都是实际项目里踩过的坑，现在处理起来顺手多了。
      
      你们项目的JWT方案是怎样的？
      `
      },
      {
        title: 'package.json依赖管理：版本锁定和安全更新',
        slug: 'npm-dependency-management-guide',
        excerpt: 'npm install总是出问题？聊聊依赖版本、lock文件、安全漏洞这些事。',
        category: '工具推荐',
        tags: ['npm', '依赖管理', '工程化'],
        status: 'published',
        isFeatured: false,
        views: 1560,
        likes: 112,
        publishedAt: new Date('2025-09-18'),
        content: `
      最近处理了几次依赖相关的问题，整理下经验。
      
      ## 版本号的含义
      
      先理解下npm的版本规则：
      
      \`\`\`json
      {
        "dependencies": {
          "express": "^4.18.0",
          "lodash": "~4.17.21",
          "react": "18.2.0"
        }
      }
      \`\`\`
      
      **^4.18.0** - 兼容版本
      - 允许更新：4.18.1, 4.19.0, 4.99.0
      - 不允许：5.0.0
      - 主版本不变
      
      **~4.17.21** - 补丁版本
      - 允许更新：4.17.22, 4.17.99
      - 不允许：4.18.0
      - 次版本不变
      
      **18.2.0** - 精确版本
      - 只安装这个版本
      
      我的建议：
      - 开发依赖用^，可以自动获取新功能
      - 生产依赖用精确版本，稳定优先
      
      ## package-lock.json很重要
      
      之前有同事把lock文件加到.gitignore了，结果每个人安装的版本都不一样。
      
      **一定要提交lock文件**
      
      \`\`\`bash
      # .gitignore
      node_modules/
      # 不要忽略lock文件！
      # package-lock.json  ❌
      \`\`\`
      
      lock文件保证了：
      - 所有人安装的版本一致
      - CI/CD环境和本地一致
      - 可以回溯到任何历史版本
      
      ## 更新依赖的方法
      
      **方式1：手动更新**
      
      \`\`\`bash
      # 查看可更新的包
      npm outdated
      
      # 更新某个包
      npm update express
      
      # 更新到最新版本（包括大版本）
      npm install express@latest
      \`\`\`
      
      **方式2：用npm-check-updates**
      
      \`\`\`bash
      # 安装
      npm install -g npm-check-updates
      
      # 检查可更新的包
      ncu
      
      # 更新package.json（不会安装）
      ncu -u
      
      # 然后安装
      npm install
      \`\`\`
      
      我通常是每个月手动更新一次，不用自动更新。更新前先：
      1. 看changelog有没有breaking changes
      2. 在测试环境验证
      3. 确认没问题再上线
      
      ## 处理依赖冲突
      
      有时候会遇到依赖冲突：
      
      \`\`\`bash
      npm ERR! Could not resolve dependency:
      npm ERR! peer react@"^18.0.0" from react-dom@18.2.0
      \`\`\`
      
      **原因**：某个包要求的peer dependency版本不匹配
      
      **解决办法**：
      
      \`\`\`bash
      # 1. 查看冲突详情
      npm ls react
      
      # 2. 强制安装（不推荐）
      npm install --force
      
      # 3. 使用legacy peer deps（推荐）
      npm install --legacy-peer-deps
      
      # 4. 或者在.npmrc里配置
      echo "legacy-peer-deps=true" >> .npmrc
      \`\`\`
      
      更好的办法是升级相关的包，让版本匹配上。
      
      ## 安全漏洞处理
      
      npm会定期扫描依赖的安全漏洞：
      
      \`\`\`bash
      # 检查漏洞
      npm audit
      
      # 自动修复（只修复兼容的版本）
      npm audit fix
      
      # 强制修复（可能有breaking changes）
      npm audit fix --force
      \`\`\`
      
      遇到无法自动修复的漏洞：
      
      **方案1：等待上游修复**
      
      如果漏洞在间接依赖里，只能等直接依赖更新。
      
      **方案2：使用resolutions（yarn）**
      
      \`\`\`json
      {
        "resolutions": {
          "axios": "1.6.0"  // 强制所有axios都用这个版本
        }
      }
      \`\`\`
      
      npm也可以用overrides（npm 8.3+）：
      
      \`\`\`json
      {
        "overrides": {
          "axios": "1.6.0"
        }
      }
      \`\`\`
      
      **方案3：切换到安全的替代包**
      
      比如moment.js有漏洞且不维护了，就换成dayjs。
      
      ## 减小node_modules体积
      
      有个项目的node_modules居然2GB，太夸张了。
      
      **查看占用最大的包**：
      
      \`\`\`bash
      # 安装工具
      npm install -g npm-consider
      
      # 分析依赖
      npx npm-consider install
      \`\`\`
      
      **优化方法**：
      
      1. 移除不用的包
      \`\`\`bash
      # 查找未使用的依赖
      npm install -g depcheck
      depcheck
      \`\`\`
      
      2. 只安装生产依赖
      \`\`\`bash
      npm install --production
      \`\`\`
      
      3. 使用pnpm（推荐）
      \`\`\`bash
      # pnpm使用硬链接，节省空间
      npm install -g pnpm
      pnpm install
      \`\`\`
      
      我现在新项目都用pnpm了，同样的依赖，node_modules能小70%。
      
      ## CI/CD的依赖安装
      
      在CI里要用\`npm ci\`而不是\`npm install\`：
      
      \`\`\`bash
      # ❌ 不要用
      npm install
      
      # ✅ 用这个
      npm ci
      \`\`\`
      
      \`npm ci\`的优点：
      - 更快（不计算依赖，直接按lock安装）
      - 更可靠（lock和package.json不匹配会报错）
      - 会先删除node_modules（保证干净环境）
      
      ## 私有包管理
      
      公司内部的私有包可以用npm私有仓库：
      
      \`\`\`.npmrc
      # .npmrc
      registry=https://registry.npm.taobao.org
      @mycompany:registry=https://npm.company.com
      \`\`\`
      
      这样：
      - 公开包从淘宝镜像安装（快）
      - 私有包从公司仓库安装
      
      ## Monorepo的依赖管理
      
      多个包在一个仓库里，可以用workspaces：
      
      \`\`\`json
      {
        "name": "my-project",
        "private": true,
        "workspaces": [
          "packages/*"
        ]
      }
      \`\`\`
      
      这样所有packages共享node_modules，不会重复安装。
      
      pnpm的workspace支持更好：
      
      \`\`\`yaml
      # pnpm-workspace.yaml
      packages:
        - 'packages/*'
        - 'apps/*'
      \`\`\`
      
      ## 一些实用命令
      
      \`\`\`bash
      # 查看包的详细信息
      npm view express
      
      # 查看某个包的所有版本
      npm view express versions
      
      # 清理缓存
      npm cache clean --force
      
      # 重新安装所有依赖
      rm -rf node_modules package-lock.json
      npm install
      
      # 查看当前使用的registry
      npm config get registry
      
      # 临时使用淘宝镜像
      npm install --registry=https://registry.npmmirror.com
      \`\`\`
      
      ## 我的依赖管理习惯
      
      1. 每个月检查一次可更新的包
      2. 重要依赖更新前先看changelog
      3. 测试环境验证后再上生产
      4. lock文件一定要提交
      5. CI用npm ci而不是install
      6. 新项目直接用pnpm
      
      依赖管理看起来简单，但坑还挺多的。这些经验是踩了不少坑总结出来的。
      
      你们团队的依赖管理流程是怎样的？
      `
      },
      {
        title: '前端监控实践：错误捕获、性能监控和用户行为追踪',
        slug: 'frontend-monitoring-implementation',
        excerpt: '线上总是出问题，但用户反馈时已经无法复现。搭建一套前端监控系统后，问题定位快多了。',
        category: '前端开发',
        tags: ['监控', '错误上报', '性能'],
        status: 'published',
        isFeatured: false,
        views: 2120,
        likes: 167,
        publishedAt: new Date('2025-09-15'),
        content: `
      之前线上出问题，用户说"报错了"，但没截图没描述，根本不知道什么情况。后来搭了监控系统，现在能看到用户操作录像和详细的错误信息。
      
      ## 错误捕获
      
      ### JavaScript错误
      
      \`\`\`javascript
      // 全局错误捕获
      window.addEventListener('error', (event) => {
        const errorInfo = {
          type: 'javascript',
          message: event.message,
          filename: event.filename,
          lineno: event.lineno,
          colno: event.colno,
          stack: event.error?.stack,
          timestamp: Date.now(),
          url: window.location.href,
          userAgent: navigator.userAgent
        };
        
        reportError(errorInfo);
      }, true);
      
      // Promise错误捕获
      window.addEventListener('unhandledrejection', (event) => {
        const errorInfo = {
          type: 'promise',
          message: event.reason?.message || event.reason,
          stack: event.reason?.stack,
          timestamp: Date.now(),
          url: window.location.href
        };
        
        reportError(errorInfo);
      });
      \`\`\`
      
      ### 资源加载错误
      
      \`\`\`javascript
      window.addEventListener('error', (event) => {
        // 判断是否是资源加载错误
        const target = event.target;
        if (target !== window && (target.tagName === 'IMG' || target.tagName === 'SCRIPT')) {
          const errorInfo = {
            type: 'resource',
            tagName: target.tagName,
            src: target.src || target.href,
            timestamp: Date.now(),
            url: window.location.href
          };
          
          reportError(errorInfo);
        }
      }, true);
      \`\`\`
      
      ### React错误边界
      
      \`\`\`jsx
      class ErrorBoundary extends React.Component {
        constructor(props) {
          super(props);
          this.state = { hasError: false };
        }
        
        static getDerivedStateFromError(error) {
          return { hasError: true };
        }
        
        componentDidCatch(error, errorInfo) {
          const errorData = {
            type: 'react',
            message: error.message,
            stack: error.stack,
            componentStack: errorInfo.componentStack,
            timestamp: Date.now(),
            url: window.location.href
          };
          
          reportError(errorData);
        }
        
        render() {
          if (this.state.hasError) {
            return <div>出错了，请刷新页面</div>;
          }
          
          return this.props.children;
        }
      }
      
      // 使用
      <ErrorBoundary>
        <App />
      </ErrorBoundary>
      \`\`\`
      
      ### Vue错误捕获
      
      \`\`\`javascript
      // Vue 3
      app.config.errorHandler = (err, instance, info) => {
        const errorInfo = {
          type: 'vue',
          message: err.message,
          stack: err.stack,
          info: info,
          timestamp: Date.now()
        };
        
        reportError(errorInfo);
      };
      
      // Vue 2
      Vue.config.errorHandler = function (err, vm, info) {
        reportError({
          type: 'vue',
          message: err.message,
          stack: err.stack,
          info: info
        });
      };
      \`\`\`
      
      ## 错误上报
      
      ### 防抖处理
      
      避免短时间内重复上报：
      
      \`\`\`javascript
      const errorMap = new Map();
      
      function reportError(errorInfo) {
        const key = \`\${errorInfo.type}-\${errorInfo.message}\`;
        const lastTime = errorMap.get(key);
        const now = Date.now();
        
        // 5秒内相同错误只上报一次
        if (lastTime && now - lastTime < 5000) {
          return;
        }
        
        errorMap.set(key, now);
        sendError(errorInfo);
      }
      \`\`\`
      
      ### 批量上报
      
      \`\`\`javascript
      class ErrorReporter {
        constructor() {
          this.errors = [];
          this.timer = null;
          this.maxSize = 10;  // 最多缓存10条
          this.maxWait = 5000; // 最长等待5秒
        }
        
        add(error) {
          this.errors.push({
            ...error,
            sessionId: this.getSessionId(),
            userId: this.getUserId()
          });
          
          // 达到最大数量，立即上报
          if (this.errors.length >= this.maxSize) {
            this.flush();
            return;
          }
          
          // 否则延迟上报
          if (!this.timer) {
            this.timer = setTimeout(() => {
              this.flush();
            }, this.maxWait);
          }
        }
        
        flush() {
          if (this.errors.length === 0) return;
          
          const errors = [...this.errors];
          this.errors = [];
          
          if (this.timer) {
            clearTimeout(this.timer);
            this.timer = null;
          }
          
          // 使用sendBeacon，页面关闭也能上报
          const url = '/api/errors/report';
          const data = JSON.stringify({ errors });
          
          if (navigator.sendBeacon) {
            navigator.sendBeacon(url, data);
          } else {
            fetch(url, {
              method: 'POST',
              body: data,
              headers: { 'Content-Type': 'application/json' },
              keepalive: true
            }).catch(console.error);
          }
        }
        
        getSessionId() {
          let sessionId = sessionStorage.getItem('sessionId');
          if (!sessionId) {
            sessionId = \`session_\${Date.now()}_\${Math.random()}\`;
            sessionStorage.setItem('sessionId', sessionId);
          }
          return sessionId;
        }
        
        getUserId() {
          // 从token或localStorage获取用户ID
          return localStorage.getItem('userId') || 'anonymous';
        }
      }
      
      const reporter = new ErrorReporter();
      
      function reportError(error) {
        reporter.add(error);
      }
      
      // 页面卸载时上报剩余错误
      window.addEventListener('beforeunload', () => {
        reporter.flush();
      });
      \`\`\`
      
      ## 性能监控
      
      ### 页面加载性能
      
      \`\`\`javascript
      window.addEventListener('load', () => {
        // 等待一段时间确保所有资源加载完成
        setTimeout(() => {
          const timing = performance.timing;
          
          const metrics = {
            // DNS查询时间
            dns: timing.domainLookupEnd - timing.domainLookupStart,
            // TCP连接时间
            tcp: timing.connectEnd - timing.connectStart,
            // 请求时间
            request: timing.responseStart - timing.requestStart,
            // 响应时间
            response: timing.responseEnd - timing.responseStart,
            // DOM解析时间
            domParse: timing.domInteractive - timing.responseEnd,
            // 资源加载时间
            resourceLoad: timing.loadEventStart - timing.domContentLoadedEventEnd,
            // 首屏时间（白屏时间）
            firstPaint: timing.responseEnd - timing.fetchStart,
            // 完全加载时间
            loadComplete: timing.loadEventEnd - timing.fetchStart
          };
          
          reportPerformance(metrics);
        }, 0);
      });
      \`\`\`
      
      ### Core Web Vitals
      
      \`\`\`javascript
      // 需要安装 web-vitals
      import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
      
      function reportWebVitals() {
        getCLS(metric => {
          console.log('CLS:', metric.value);
          reportPerformance({ cls: metric.value });
        });
        
        getFID(metric => {
          console.log('FID:', metric.value);
          reportPerformance({ fid: metric.value });
        });
        
        getFCP(metric => {
          console.log('FCP:', metric.value);
          reportPerformance({ fcp: metric.value });
        });
        
        getLCP(metric => {
          console.log('LCP:', metric.value);
          reportPerformance({ lcp: metric.value });
        });
        
        getTTFB(metric => {
          console.log('TTFB:', metric.value);
          reportPerformance({ ttfb: metric.value });
        });
      }
      
      reportWebVitals();
      \`\`\`
      
      ### 接口性能监控
      
      \`\`\`javascript
      // 拦截fetch
      const originalFetch = window.fetch;
      window.fetch = function(...args) {
        const startTime = Date.now();
        const url = args[0];
        
        return originalFetch.apply(this, args)
          .then(response => {
            const duration = Date.now() - startTime;
            
            reportAPI({
              url,
              method: args[1]?.method || 'GET',
              status: response.status,
              duration,
              timestamp: Date.now()
            });
            
            return response;
          })
          .catch(error => {
            const duration = Date.now() - startTime;
            
            reportAPI({
              url,
              method: args[1]?.method || 'GET',
              error: error.message,
              duration,
              timestamp: Date.now()
            });
            
            throw error;
          });
      };
      
      // 或使用axios拦截器
      axios.interceptors.request.use(config => {
        config.metadata = { startTime: Date.now() };
        return config;
      });
      
      axios.interceptors.response.use(
        response => {
          const duration = Date.now() - response.config.metadata.startTime;
          
          reportAPI({
            url: response.config.url,
            method: response.config.method,
            status: response.status,
            duration
          });
          
          return response;
        },
        error => {
          const duration = Date.now() - error.config?.metadata?.startTime;
          
          reportAPI({
            url: error.config?.url,
            method: error.config?.method,
            error: error.message,
            duration
          });
          
          return Promise.reject(error);
        }
      );
      \`\`\`
      
      ## 用户行为追踪
      
      ### 页面访问统计
      
      \`\`\`javascript
      // 路由变化时上报
      // React Router
      import { useLocation } from 'react-router-dom';
      
      function usePageTracking() {
        const location = useLocation();
        
        useEffect(() => {
          reportPageView({
            path: location.pathname,
            title: document.title,
            referrer: document.referrer,
            timestamp: Date.now()
          });
        }, [location]);
      }
      
      // Vue Router
      router.afterEach((to) => {
        reportPageView({
          path: to.path,
          title: document.title,
          timestamp: Date.now()
        });
      });
      \`\`\`
      
      ### 点击事件追踪
      
      \`\`\`javascript
      // 监听所有点击事件
      document.addEventListener('click', (event) => {
        const target = event.target;
        
        // 获取点击元素的信息
        const elementInfo = {
          tagName: target.tagName,
          id: target.id,
          className: target.className,
          text: target.innerText?.substring(0, 50),
          path: getElementPath(target)
        };
        
        reportClick({
          ...elementInfo,
          x: event.pageX,
          y: event.pageY,
          timestamp: Date.now()
        });
      }, true);
      
      // 获取元素的DOM路径
      function getElementPath(element) {
        const path = [];
        while (element && element.nodeType === Node.ELEMENT_NODE) {
          let selector = element.nodeName.toLowerCase();
          if (element.id) {
            selector += '#' + element.id;
          } else if (element.className) {
            selector += '.' + element.className.split(' ').join('.');
          }
          path.unshift(selector);
          element = element.parentNode;
        }
        return path.join(' > ');
      }
      \`\`\`
      
      ### 停留时间统计
      
      \`\`\`javascript
      let pageStartTime = Date.now();
      let isPageVisible = !document.hidden;
      
      // 页面可见性变化
      document.addEventListener('visibilitychange', () => {
        if (document.hidden) {
          // 页面隐藏，记录停留时间
          const duration = Date.now() - pageStartTime;
          reportDuration(duration);
          isPageVisible = false;
        } else {
          // 页面显示，重新计时
          pageStartTime = Date.now();
          isPageVisible = true;
        }
      });
      
      // 页面卸载时上报
      window.addEventListener('beforeunload', () => {
        if (isPageVisible) {
          const duration = Date.now() - pageStartTime;
          reportDuration(duration);
        }
      });
      \`\`\`
      
      ## 完整的监控SDK
      
      \`\`\`javascript
      class Monitor {
        constructor(options = {}) {
          this.apiUrl = options.apiUrl;
          this.appId = options.appId;
          this.userId = options.userId;
          
          this.init();
        }
        
        init() {
          this.setupErrorCapture();
          this.setupPerformanceMonitor();
          this.setupBehaviorTracking();
        }
        
        setupErrorCapture() {
          // 错误捕获逻辑
        }
        
        setupPerformanceMonitor() {
          // 性能监控逻辑
        }
        
        setupBehaviorTracking() {
          // 行为追踪逻辑
        }
        
        setUserId(userId) {
          this.userId = userId;
        }
        
        // 手动上报
        reportEvent(eventName, data) {
          this.send({
            type: 'event',
            eventName,
            data,
            timestamp: Date.now()
          });
        }
        
        send(data) {
          const payload = {
            ...data,
            appId: this.appId,
            userId: this.userId,
            url: window.location.href,
            userAgent: navigator.userAgent
          };
          
          if (navigator.sendBeacon) {
            navigator.sendBeacon(this.apiUrl, JSON.stringify(payload));
          } else {
            fetch(this.apiUrl, {
              method: 'POST',
              body: JSON.stringify(payload),
              headers: { 'Content-Type': 'application/json' },
              keepalive: true
            }).catch(console.error);
          }
        }
      }
      
      // 使用
      const monitor = new Monitor({
        apiUrl: 'https://api.example.com/monitor',
        appId: 'your-app-id',
        userId: localStorage.getItem('userId')
      });
      
      // 登录后设置用户ID
      monitor.setUserId(user.id);
      
      // 手动上报事件
      monitor.reportEvent('购买成功', { 
        productId: 123, 
        amount: 99 
      });
      \`\`\`
      
      ## 后端接收和处理
      
      \`\`\`javascript
      // 简单的接收接口
      app.post('/api/monitor', async (req, res) => {
        const data = req.body;
        
        // 保存到数据库或发送到日志系统
        await MonitorLog.create(data);
        
        // 严重错误发送告警
        if (data.type === 'error' && isCriticalError(data)) {
          sendAlert(data);
        }
        
        res.status(200).end();
      });
      \`\`\`
      
      有了监控系统，定位问题方便多了。建议生产环境一定要有。
      
      开源方案推荐：Sentry（错误监控）、Google Analytics（用户行为）、Web Vitals（性能监控）。
      
      你们项目用什么监控方案？
      `
      },
      {
        title: 'JavaScript核心知识点笔记整理',
        slug: 'javascript-core-concepts-notes',
        excerpt: '整理了JavaScript中常用的核心概念和技巧，包括作用域、闭包、异步、原型链等。',
        category: '学习笔记',
        tags: ['JavaScript', '知识点', '笔记'],
        status: 'published',
        isFeatured: false,
        views: 4560,
        likes: 378,
        publishedAt: new Date('2025-09-12'),
        content: `
      整理下JavaScript的核心知识点，方便查阅。
      
      ## 1. 变量声明
      
      \`\`\`javascript
      // var - 函数作用域，会提升
      var a = 1;
      
      // let - 块级作用域，不会提升
      let b = 2;
      
      // const - 块级作用域，不可重新赋值
      const c = 3;
      
      // 示例
      for (var i = 0; i < 3; i++) {
        setTimeout(() => console.log(i), 100);
      }
      // 输出：3 3 3（var没有块级作用域）
      
      for (let i = 0; i < 3; i++) {
        setTimeout(() => console.log(i), 100);
      }
      // 输出：0 1 2（let有块级作用域）
      \`\`\`
      
      ## 2. 数据类型
      
      **基本类型**：
      - String
      - Number
      - Boolean
      - Null
      - Undefined
      - Symbol
      - BigInt
      
      **引用类型**：
      - Object
      - Array
      - Function
      - Date
      - RegExp
      
      \`\`\`javascript
      // 类型检查
      typeof 'hello'        // 'string'
      typeof 123           // 'number'
      typeof true          // 'boolean'
      typeof undefined     // 'undefined'
      typeof null          // 'object' (历史遗留bug)
      typeof Symbol()      // 'symbol'
      typeof {}            // 'object'
      typeof []            // 'object'
      typeof function(){}  // 'function'
      
      // 更准确的类型检查
      Object.prototype.toString.call([])        // '[object Array]'
      Object.prototype.toString.call({})        // '[object Object]'
      Object.prototype.toString.call(null)      // '[object Null]'
      
      // 判断数组
      Array.isArray([])     // true
      \`\`\`
      
      ## 3. 作用域和闭包
      
      \`\`\`javascript
      // 闭包：函数可以访问外部变量
      function outer() {
        let count = 0;
        
        return function inner() {
          count++;
          return count;
        };
      }
      
      const counter = outer();
      console.log(counter()); // 1
      console.log(counter()); // 2
      
      // 常见应用：私有变量
      function createPerson(name) {
        let age = 0; // 私有变量
        
        return {
          getName: () => name,
          getAge: () => age,
          setAge: (newAge) => {
            if (newAge > 0) age = newAge;
          }
        };
      }
      
      const person = createPerson('John');
      person.setAge(25);
      console.log(person.getAge()); // 25
      // age无法直接访问
      \`\`\`
      
      ## 4. this指向
      
      \`\`\`javascript
      // 1. 全局上下文
      console.log(this); // window（浏览器）或 global（Node.js）
      
      // 2. 对象方法
      const obj = {
        name: 'John',
        sayName: function() {
          console.log(this.name);
        }
      };
      obj.sayName(); // 'John'
      
      // 3. 箭头函数（继承外层this）
      const obj2 = {
        name: 'John',
        sayName: () => {
          console.log(this.name);
        }
      };
      obj2.sayName(); // undefined（this指向全局）
      
      // 4. 构造函数
      function Person(name) {
        this.name = name;
      }
      const p = new Person('John');
      console.log(p.name); // 'John'
      
      // 5. call/apply/bind
      function greet() {
        console.log(\`Hello, \${this.name}\`);
      }
      const user = { name: 'John' };
      greet.call(user);   // 'Hello, John'
      greet.apply(user);  // 'Hello, John'
      const boundGreet = greet.bind(user);
      boundGreet();       // 'Hello, John'
      \`\`\`
      
      ## 5. 原型和原型链
      
      \`\`\`javascript
      // 构造函数
      function Person(name) {
        this.name = name;
      }
      
      // 原型方法
      Person.prototype.sayName = function() {
        console.log(this.name);
      };
      
      const p1 = new Person('John');
      p1.sayName(); // 'John'
      
      // 原型链
      console.log(p1.__proto__ === Person.prototype); // true
      console.log(Person.prototype.__proto__ === Object.prototype); // true
      console.log(Object.prototype.__proto__ === null); // true
      
      // ES6 Class语法糖
      class Animal {
        constructor(name) {
          this.name = name;
        }
        
        speak() {
          console.log(\`\${this.name} makes a sound\`);
        }
      }
      
      class Dog extends Animal {
        constructor(name, breed) {
          super(name);
          this.breed = breed;
        }
        
        speak() {
          console.log(\`\${this.name} barks\`);
        }
      }
      
      const dog = new Dog('Rex', 'Labrador');
      dog.speak(); // 'Rex barks'
      \`\`\`
      
      ## 6. 异步编程
      
      \`\`\`javascript
      // 1. 回调函数
      function fetchData(callback) {
        setTimeout(() => {
          callback('data');
        }, 1000);
      }
      
      // 2. Promise
      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('data');
        }, 1000);
      });
      
      promise.then(data => console.log(data));
      
      // 3. async/await
      async function getData() {
        const data = await promise;
        console.log(data);
      }
      
      // 错误处理
      async function getData() {
        try {
          const data = await fetch('/api/data');
          return data.json();
        } catch (error) {
          console.error(error);
        }
      }
      
      // Promise.all - 并行执行
      const [result1, result2] = await Promise.all([
        fetch('/api/1'),
        fetch('/api/2')
      ]);
      
      // Promise.race - 取最快的
      const result = await Promise.race([
        fetch('/api/1'),
        fetch('/api/2')
      ]);
      \`\`\`
      
      ## 7. 数组方法
      
      \`\`\`javascript
      const arr = [1, 2, 3, 4, 5];
      
      // map - 映射
      arr.map(x => x * 2); // [2, 4, 6, 8, 10]
      
      // filter - 过滤
      arr.filter(x => x > 2); // [3, 4, 5]
      
      // reduce - 归约
      arr.reduce((sum, x) => sum + x, 0); // 15
      
      // find - 查找
      arr.find(x => x > 2); // 3
      
      // findIndex - 查找索引
      arr.findIndex(x => x > 2); // 2
      
      // some - 是否有满足条件的
      arr.some(x => x > 4); // true
      
      // every - 是否全部满足条件
      arr.every(x => x > 0); // true
      
      // forEach - 遍历（无返回值）
      arr.forEach(x => console.log(x));
      
      // 链式调用
      arr
        .filter(x => x > 2)
        .map(x => x * 2)
        .reduce((sum, x) => sum + x, 0); // 24
      \`\`\`
      
      ## 8. 对象操作
      
      \`\`\`javascript
      const obj = { a: 1, b: 2, c: 3 };
      
      // 获取键
      Object.keys(obj); // ['a', 'b', 'c']
      
      // 获取值
      Object.values(obj); // [1, 2, 3]
      
      // 获取键值对
      Object.entries(obj); // [['a', 1], ['b', 2], ['c', 3]]
      
      // 合并对象
      const obj2 = { d: 4 };
      const merged = { ...obj, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }
      // 或
      const merged2 = Object.assign({}, obj, obj2);
      
      // 深拷贝
      const deepCopy = JSON.parse(JSON.stringify(obj));
      // 注意：无法拷贝函数、undefined、Symbol
      
      // 解构
      const { a, b } = obj;
      const { a: x, b: y } = obj; // 重命名
      
      // 属性简写
      const name = 'John';
      const age = 25;
      const person = { name, age }; // { name: 'John', age: 25 }
      
      // 计算属性名
      const key = 'name';
      const obj3 = {
        [key]: 'John'
      }; // { name: 'John' }
      \`\`\`
      
      ## 9. 解构赋值
      
      \`\`\`javascript
      // 数组解构
      const [a, b, c] = [1, 2, 3];
      const [first, ...rest] = [1, 2, 3, 4]; // first=1, rest=[2,3,4]
      
      // 默认值
      const [x = 0, y = 0] = [1]; // x=1, y=0
      
      // 交换变量
      let a = 1, b = 2;
      [a, b] = [b, a];
      
      // 对象解构
      const { name, age } = { name: 'John', age: 25 };
      
      // 嵌套解构
      const user = {
        info: {
          name: 'John',
          address: {
            city: 'Beijing'
          }
        }
      };
      const { info: { name, address: { city } } } = user;
      
      // 函数参数解构
      function greet({ name, age = 18 }) {
        console.log(\`\${name}, \${age}\`);
      }
      greet({ name: 'John', age: 25 });
      \`\`\`
      
      ## 10. ES6+新特性
      
      \`\`\`javascript
      // 模板字符串
      const name = 'John';
      const greeting = \`Hello, \${name}!\`;
      
      // 默认参数
      function greet(name = 'Guest') {
        console.log(\`Hello, \${name}\`);
      }
      
      // 剩余参数
      function sum(...numbers) {
        return numbers.reduce((a, b) => a + b, 0);
      }
      sum(1, 2, 3, 4); // 10
      
      // 展开运算符
      const arr1 = [1, 2, 3];
      const arr2 = [4, 5, 6];
      const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
      
      // 可选链
      const user = { info: { name: 'John' } };
      const city = user?.info?.address?.city; // undefined（不报错）
      
      // 空值合并
      const value = null ?? 'default'; // 'default'
      const value2 = 0 ?? 'default'; // 0（只有null/undefined才用默认值）
      
      // Set - 去重
      const arr = [1, 2, 2, 3, 3];
      const unique = [...new Set(arr)]; // [1, 2, 3]
      
      // Map - 更强大的对象
      const map = new Map();
      map.set('name', 'John');
      map.set(123, 'number key');
      map.get('name'); // 'John'
      map.has('name'); // true
      map.delete('name');
      map.size; // 1
      \`\`\`
      
      ## 11. 常用技巧
      
      \`\`\`javascript
      // 判断空对象
      Object.keys(obj).length === 0
      
      // 判断空数组
      arr.length === 0
      
      // 数组去重
      [...new Set(arr)]
      arr.filter((item, index) => arr.indexOf(item) === index)
      
      // 数组扁平化
      arr.flat(Infinity)
      // 或
      function flatten(arr) {
        return arr.reduce((acc, val) => 
          Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val), 
          []
        );
      }
      
      // 防抖
      function debounce(fn, delay) {
        let timer = null;
        return function(...args) {
          clearTimeout(timer);
          timer = setTimeout(() => fn.apply(this, args), delay);
        };
      }
      
      // 节流
      function throttle(fn, delay) {
        let lastTime = 0;
        return function(...args) {
          const now = Date.now();
          if (now - lastTime >= delay) {
            fn.apply(this, args);
            lastTime = now;
          }
        };
      }
      
      // 深拷贝
      function deepClone(obj) {
        if (obj === null || typeof obj !== 'object') return obj;
        if (obj instanceof Date) return new Date(obj);
        if (obj instanceof RegExp) return new RegExp(obj);
        if (obj instanceof Array) return obj.map(item => deepClone(item));
        
        const cloned = {};
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
            cloned[key] = deepClone(obj[key]);
          }
        }
        return cloned;
      }
      \`\`\`
      
      ## 12. 性能优化
      
      \`\`\`javascript
      // 1. 使用const/let代替var
      // 2. 避免全局变量
      // 3. 使用事件委托
      document.getElementById('parent').addEventListener('click', (e) => {
        if (e.target.matches('.child')) {
          // 处理点击
        }
      });
      
      // 4. 防抖节流
      input.addEventListener('input', debounce(handleInput, 300));
      
      // 5. 避免频繁DOM操作
      // 差：
      for (let i = 0; i < 1000; i++) {
        document.body.innerHTML += '<div>item</div>';
      }
      // 好：
      const fragment = document.createDocumentFragment();
      for (let i = 0; i < 1000; i++) {
        const div = document.createElement('div');
        div.textContent = 'item';
        fragment.appendChild(div);
      }
      document.body.appendChild(fragment);
      
      // 6. 使用Web Worker处理大量计算
      const worker = new Worker('worker.js');
      worker.postMessage({ data });
      worker.onmessage = (e) => {
        console.log(e.data);
      };
      \`\`\`
      
      这些是JavaScript中最常用的知识点，建议多练习，熟能生巧。
      `
      },
      {
        title: 'CSS布局技巧整理：Flex、Grid、居中方案',
        slug: 'css-layout-tips-notes',
        excerpt: '整理了CSS中常用的布局方法和技巧，包括Flex、Grid、各种居中方案等。',
        category: '学习笔记',
        tags: ['CSS', '布局', '笔记'],
        status: 'published',
        isFeatured: false,
        views: 3890,
        likes: 289,
        publishedAt: new Date('2025-09-10'),
        content: `
      CSS布局的常用技巧整理，方便快速查阅。
      
      ## 1. Flexbox布局
      
      ### 基础概念
      
      \`\`\`css
      .container {
        display: flex;
        
        /* 主轴方向 */
        flex-direction: row;        /* 水平（默认） */
        flex-direction: column;     /* 垂直 */
        
        /* 换行 */
        flex-wrap: nowrap;          /* 不换行（默认） */
        flex-wrap: wrap;            /* 换行 */
        
        /* 主轴对齐 */
        justify-content: flex-start;    /* 起点 */
        justify-content: center;        /* 居中 */
        justify-content: space-between; /* 两端对齐 */
        justify-content: space-around;  /* 均匀分布 */
        
        /* 交叉轴对齐 */
        align-items: flex-start;    /* 起点 */
        align-items: center;        /* 居中 */
        align-items: stretch;       /* 拉伸（默认） */
      }
      
      .item {
        /* 放大比例 */
        flex-grow: 1;
        
        /* 缩小比例 */
        flex-shrink: 1;
        
        /* 基础尺寸 */
        flex-basis: 200px;
        
        /* 简写 */
        flex: 1; /* flex-grow: 1; flex-shrink: 1; flex-basis: 0%; */
        
        /* 单独设置对齐 */
        align-self: center;
      }
      \`\`\`
      
      ### 常见布局
      
      \`\`\`css
      /* 1. 水平居中 */
      .container {
        display: flex;
        justify-content: center;
      }
      
      /* 2. 垂直居中 */
      .container {
        display: flex;
        align-items: center;
      }
      
      /* 3. 水平垂直居中 */
      .container {
        display: flex;
        justify-content: center;
        align-items: center;
      }
      
      /* 4. 两端对齐 */
      .container {
        display: flex;
        justify-content: space-between;
      }
      
      /* 5. 平均分布 */
      .container {
        display: flex;
      }
      .item {
        flex: 1;
      }
      
      /* 6. 左侧固定，右侧自适应 */
      .container {
        display: flex;
      }
      .left {
        width: 200px;
      }
      .right {
        flex: 1;
      }
      
      /* 7. 底部固定 */
      .container {
        display: flex;
        flex-direction: column;
        min-height: 100vh;
      }
      .content {
        flex: 1;
      }
      .footer {
        height: 60px;
      }
      \`\`\`
      
      ## 2. Grid布局
      
      ### 基础概念
      
      \`\`\`css
      .container {
        display: grid;
        
        /* 定义列 */
        grid-template-columns: 100px 200px 100px;
        grid-template-columns: 1fr 2fr 1fr;        /* fr单位 */
        grid-template-columns: repeat(3, 1fr);     /* 重复 */
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); /* 自适应 */
        
        /* 定义行 */
        grid-template-rows: 100px 200px;
        
        /* 间距 */
        gap: 20px;              /* 行列间距 */
        row-gap: 20px;          /* 行间距 */
        column-gap: 20px;       /* 列间距 */
        
        /* 对齐 */
        justify-items: center;  /* 水平对齐 */
        align-items: center;    /* 垂直对齐 */
      }
      
      .item {
        /* 占据几列 */
        grid-column: 1 / 3;     /* 从第1列到第3列 */
        grid-column: span 2;    /* 占据2列 */
        
        /* 占据几行 */
        grid-row: 1 / 3;
        grid-row: span 2;
      }
      \`\`\`
      
      ### 常见布局
      
      \`\`\`css
      /* 1. 三列等宽 */
      .container {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 20px;
      }
      
      /* 2. 圣杯布局 */
      .container {
        display: grid;
        grid-template-columns: 200px 1fr 200px;
        grid-template-rows: 60px 1fr 60px;
      }
      .header {
        grid-column: 1 / 4;
      }
      .footer {
        grid-column: 1 / 4;
      }
      
      /* 3. 响应式卡片 */
      .container {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
        gap: 20px;
      }
      
      /* 4. 瀑布流（需要grid-auto-flow: dense） */
      .container {
        display: grid;
        grid-template-columns: repeat(auto-fill, 200px);
        grid-auto-rows: 100px;
        grid-auto-flow: dense;
        gap: 10px;
      }
      .item-large {
        grid-row: span 2;
      }
      \`\`\`
      
      ## 3. 居中方案
      
      ### 水平居中
      
      \`\`\`css
      /* 1. 行内元素 */
      .parent {
        text-align: center;
      }
      
      /* 2. 块级元素 */
      .element {
        margin: 0 auto;
        width: 200px; /* 需要指定宽度 */
      }
      
      /* 3. Flex */
      .parent {
        display: flex;
        justify-content: center;
      }
      
      /* 4. Grid */
      .parent {
        display: grid;
        justify-items: center;
      }
      \`\`\`
      
      ### 垂直居中
      
      \`\`\`css
      /* 1. 单行文本 */
      .element {
        height: 100px;
        line-height: 100px;
      }
      
      /* 2. Flex */
      .parent {
        display: flex;
        align-items: center;
      }
      
      /* 3. Grid */
      .parent {
        display: grid;
        align-items: center;
      }
      
      /* 4. Position + Transform */
      .element {
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
      }
      \`\`\`
      
      ### 水平垂直居中
      
      \`\`\`css
      /* 1. Flex（推荐） */
      .parent {
        display: flex;
        justify-content: center;
        align-items: center;
      }
      
      /* 2. Grid */
      .parent {
        display: grid;
        place-items: center; /* justify-items + align-items */
      }
      
      /* 3. Position + Transform */
      .element {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
      
      /* 4. Position + Margin */
      .element {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        margin: auto;
        width: 200px;  /* 需要指定宽高 */
        height: 100px;
      }
      \`\`\`
      
      ## 4. 响应式布局
      
      \`\`\`css
      /* 媒体查询 */
      @media (max-width: 768px) {
        .container {
          flex-direction: column;
        }
      }
      
      /* 容器查询（新特性） */
      .container {
        container-type: inline-size;
      }
      @container (min-width: 400px) {
        .item {
          display: grid;
          grid-template-columns: 1fr 1fr;
        }
      }
      
      /* clamp函数：响应式字体 */
      .text {
        font-size: clamp(16px, 2vw, 24px);
        /* 最小16px，理想2vw，最大24px */
      }
      
      /* min/max函数 */
      .element {
        width: min(500px, 100%); /* 取较小值 */
        width: max(200px, 50%);  /* 取较大值 */
      }
      \`\`\`
      
      ## 5. 常用技巧
      
      ### 文本溢出
      
      \`\`\`css
      /* 单行溢出 */
      .text {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      
      /* 多行溢出 */
      .text {
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
        overflow: hidden;
      }
      \`\`\`
      
      ### 清除浮动
      
      \`\`\`css
      /* 方法1 */
      .clearfix::after {
        content: '';
        display: table;
        clear: both;
      }
      
      /* 方法2 */
      .container {
        overflow: hidden;
      }
      \`\`\`
      
      ### 宽高比
      
      \`\`\`css
      /* 方法1：aspect-ratio */
      .box {
        aspect-ratio: 16 / 9;
      }
      
      /* 方法2：padding */
      .box {
        padding-top: 56.25%; /* 9/16 = 0.5625 */
        position: relative;
      }
      .box-content {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
      }
      \`\`\`
      
      ### 滚动吸顶
      
      \`\`\`css
      .sticky-header {
        position: sticky;
        top: 0;
        background: white;
        z-index: 100;
      }
      \`\`\`
      
      ### 毛玻璃效果
      
      \`\`\`css
      .glass {
        background: rgba(255, 255, 255, 0.1);
        backdrop-filter: blur(10px);
        border: 1px solid rgba(255, 255, 255, 0.2);
      }
      \`\`\`
      
      ### 渐变背景
      
      \`\`\`css
      /* 线性渐变 */
      .gradient {
        background: linear-gradient(to right, #667eea, #764ba2);
      }
      
      /* 径向渐变 */
      .gradient {
        background: radial-gradient(circle, #667eea, #764ba2);
      }
      
      /* 渐变文字 */
      .gradient-text {
        background: linear-gradient(to right, #667eea, #764ba2);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
      }
      \`\`\`
      
      ### 动画
      
      \`\`\`css
      /* 过渡 */
      .button {
        transition: all 0.3s ease;
      }
      .button:hover {
        transform: scale(1.05);
      }
      
      /* 关键帧动画 */
      @keyframes fadeIn {
        from {
          opacity: 0;
          transform: translateY(20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }
      
      .element {
        animation: fadeIn 0.5s ease;
      }
      \`\`\`
      
      ## 6. 性能优化
      
      \`\`\`css
      /* 1. 使用transform代替position */
      /* 差 */
      .element {
        position: relative;
        left: 100px;
      }
      
      /* 好 */
      .element {
        transform: translateX(100px);
      }
      
      /* 2. 使用will-change提示浏览器 */
      .element {
        will-change: transform, opacity;
      }
      
      /* 3. 避免使用@import */
      /* 差 */
      @import url('other.css');
      
      /* 好：在HTML中用link */
      <link rel="stylesheet" href="other.css">
      
      /* 4. 减少选择器层级 */
      /* 差 */
      .container .list .item .title { }
      
      /* 好 */
      .item-title { }
      \`\`\`
      
      这些是CSS中最实用的布局技巧，掌握了基本够用了。
      `
      },
      {
        title: 'Linux命令速查笔记：服务器运维必备',
        slug: 'linux-commands-cheatsheet',
        excerpt: '整理了Linux服务器运维中最常用的命令，包括文件操作、进程管理、网络诊断等。',
        category: '学习笔记',
        tags: ['Linux', '运维', '命令行'],
        status: 'published',
        isFeatured: false,
        views: 3240,
        likes: 245,
        publishedAt: new Date('2025-09-08'),
        content: `
      整理服务器运维常用的Linux命令，方便查阅。
      
      ## 1. 文件操作
      
      \`\`\`bash
      # 查看文件
      ls -lah          # 详细列表，包括隐藏文件
      ls -lh --sort=size  # 按大小排序
      
      # 查看文件内容
      cat file.txt     # 全部内容
      head -n 20 file.txt  # 前20行
      tail -n 20 file.txt  # 后20行
      tail -f file.txt # 实时查看（日志）
      
      # 搜索文件
      find . -name "*.js"              # 按名称查找
      find . -type f -size +100M       # 查找大于100M的文件
      find . -mtime -7                 # 7天内修改的文件
      
      # 搜索文件内容
      grep "error" file.txt            # 搜索关键词
      grep -r "TODO" .                 # 递归搜索
      grep -i "error" file.txt         # 忽略大小写
      grep -n "error" file.txt         # 显示行号
      
      # 文件操作
      cp source.txt dest.txt           # 复制
      cp -r dir1 dir2                  # 复制目录
      mv old.txt new.txt               # 移动/重命名
      rm file.txt                      # 删除文件
      rm -rf directory                 # 删除目录
      
      # 创建
      mkdir dir                        # 创建目录
      mkdir -p parent/child            # 创建多级目录
      touch file.txt                   # 创建空文件
      
      # 查看磁盘使用
      df -h                            # 磁盘使用情况
      du -sh directory                 # 目录大小
      du -sh * | sort -h               # 当前目录各文件夹大小排序
      \`\`\`
      
      ## 2. 进程管理
      
      \`\`\`bash
      # 查看进程
      ps aux                           # 所有进程
      ps aux | grep node               # 查找node进程
      top                              # 实时进程监控
      htop                             # 更友好的top（需安装）
      
      # 杀死进程
      kill PID                         # 正常终止
      kill -9 PID                      # 强制终止
      killall node                     # 杀死所有node进程
      pkill -f "node server.js"        # 按名称杀死
      
      # 后台运行
      nohup command &                  # 后台运行，不受终端关闭影响
      command &                        # 后台运行
      jobs                             # 查看后台任务
      fg %1                            # 将后台任务调到前台
      
      # PM2管理（Node.js）
      pm2 start app.js                 # 启动
      pm2 list                         # 列表
      pm2 logs                         # 日志
      pm2 restart app                  # 重启
      pm2 stop app                     # 停止
      pm2 delete app                   # 删除
      \`\`\`
      
      ## 3. 系统信息
      
      \`\`\`bash
      # 系统信息
      uname -a                         # 系统信息
      cat /etc/os-release              # 系统版本
      uptime                           # 运行时间
      free -h                          # 内存使用
      vmstat                           # 系统状态
      
      # CPU信息
      lscpu                            # CPU信息
      cat /proc/cpuinfo                # 详细CPU信息
      
      # 内存信息
      cat /proc/meminfo                # 详细内存信息
      
      # 网络信息
      ifconfig                         # 网络接口
      ip addr                          # IP地址
      hostname -I                      # 本机IP
      \`\`\`
      
      ## 4. 网络相关
      
      \`\`\`bash
      # 端口占用
      netstat -tulpn | grep :3000      # 查看端口占用
      lsof -i :3000                    # 查看端口占用（更详细）
      ss -tulpn | grep :3000           # 查看端口占用（推荐）
      
      # 网络测试
      ping baidu.com                   # ping测试
      curl https://api.example.com     # 请求测试
      wget https://example.com/file    # 下载文件
      telnet host port                 # 测试端口连通性
      nc -zv host port                 # 测试端口（推荐）
      
      # 查看连接
      netstat -an                      # 所有连接
      ss -s                            # 连接统计
      
      # 防火墙
      ufw status                       # 查看状态
      ufw allow 80                     # 开放端口
      ufw deny 80                      # 关闭端口
      ufw enable                       # 启用防火墙
      \`\`\`
      
      ## 5. 文本处理
      
      \`\`\`bash
      # sed - 文本替换
      sed 's/old/new/g' file.txt       # 替换（不修改文件）
      sed -i 's/old/new/g' file.txt    # 替换（修改文件）
      sed -n '10,20p' file.txt         # 打印10-20行
      
      # awk - 文本分析
      awk '{print $1}' file.txt        # 打印第一列
      awk -F: '{print $1}' /etc/passwd # 指定分隔符
      ps aux | awk '{print $2, $11}'   # 打印进程ID和命令
      
      # sort - 排序
      sort file.txt                    # 排序
      sort -r file.txt                 # 倒序
      sort -n file.txt                 # 数字排序
      
      # uniq - 去重
      sort file.txt | uniq             # 去重（需先排序）
      sort file.txt | uniq -c          # 统计重复次数
      
      # wc - 统计
      wc -l file.txt                   # 行数
      wc -w file.txt                   # 单词数
      wc -c file.txt                   # 字节数
      \`\`\`
      
      ## 6. 压缩解压
      
      \`\`\`bash
      # tar
      tar -czf archive.tar.gz files    # 压缩
      tar -xzf archive.tar.gz          # 解压
      tar -xzf archive.tar.gz -C /path # 解压到指定目录
      tar -tzf archive.tar.gz          # 查看内容
      
      # zip
      zip -r archive.zip files         # 压缩
      unzip archive.zip                # 解压
      unzip -l archive.zip             # 查看内容
      
      # gzip
      gzip file.txt                    # 压缩
      gunzip file.txt.gz               # 解压
      \`\`\`
      
      ## 7. 权限管理
      
      \`\`\`bash
      # 权限
      chmod 755 file.txt               # rwxr-xr-x
      chmod +x file.txt                # 添加执行权限
      chmod -R 755 directory           # 递归修改
      
      # 所有者
      chown user file.txt              # 修改所有者
      chown user:group file.txt        # 修改所有者和组
      chown -R user directory          # 递归修改
      
      # 查看权限
      ls -l file.txt
      \`\`\`
      
      ## 8. SSH相关
      
      \`\`\`bash
      # 连接
      ssh user@host                    # 基本连接
      ssh -p 2222 user@host            # 指定端口
      ssh -i key.pem user@host         # 使用密钥
      
      # 文件传输
      scp file.txt user@host:/path     # 上传文件
      scp -r directory user@host:/path # 上传目录
      scp user@host:/path/file.txt .   # 下载文件
      
      # 免密登录
      ssh-keygen                       # 生成密钥
      ssh-copy-id user@host            # 复制公钥到服务器
      \`\`\`
      
      ## 9. 日志查看
      
      \`\`\`bash
      # 系统日志
      tail -f /var/log/syslog          # 系统日志
      tail -f /var/log/auth.log        # 认证日志
      journalctl -f                    # systemd日志
      
      # Nginx日志
      tail -f /var/log/nginx/access.log
      tail -f /var/log/nginx/error.log
      
      # 搜索日志
      grep "error" /var/log/nginx/error.log
      grep "404" /var/log/nginx/access.log | wc -l  # 统计404数量
      \`\`\`
      
      ## 10. 性能分析
      
      \`\`\`bash
      # CPU
      top                              # 实时CPU使用
      mpstat                           # CPU统计
      
      # 内存
      free -h                          # 内存使用
      vmstat 1                         # 每秒输出一次
      
      # 磁盘IO
      iostat                           # IO统计
      iotop                            # 实时IO监控
      
      # 网络
      iftop                            # 网络流量监控
      nethogs                          # 按进程查看网络使用
      \`\`\`
      
      ## 11. 定时任务
      
      \`\`\`bash
      # crontab
      crontab -e                       # 编辑定时任务
      crontab -l                       # 查看定时任务
      
      # 格式：分 时 日 月 周 命令
      # 示例
      0 2 * * * /path/to/backup.sh     # 每天2点执行
      */10 * * * * /path/to/check.sh   # 每10分钟执行
      0 0 * * 0 /path/to/weekly.sh     # 每周日0点执行
      \`\`\`
      
      ## 12. 实用组合
      
      \`\`\`bash
      # 查找并删除大文件
      find . -type f -size +100M -delete
      
      # 查看占用端口最多的进程
      netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n

      
      # 统计代码行数
      find . -name "*.js" | xargs wc -l
      
      # 查看目录下最大的10个文件
      du -ah . | sort -rh | head -10
      
      # 监控命令输出
      watch -n 1 "ps aux | grep node"
      
      # 查看最占内存的10个进程
      ps aux | sort -k4 -r | head -10
      
      # 查看最占CPU的10个进程
      ps aux | sort -k3 -r | head -10
      \`\`\`
      
      这些是日常运维中最常用的命令，建议多练习。
      `
      },
      {
        title: '2025前端面试题汇总：JavaScript基础必考100题',
        slug: '2025-frontend-interview-javascript-basics',
        excerpt: '整理了前端面试中JavaScript基础部分的高频考题，包括原型链、闭包、异步等核心知识点。',
        category: '面试准备',
        tags: ['面试', 'JavaScript', '前端'],
        status: 'published',
        isFeatured: true,
        views: 8920,
        likes: 756,
        publishedAt: new Date('2025-09-05'),
        content: `
      整理了最近面试遇到的JavaScript基础题，这些都是高频考点。
      
      ## 一、数据类型（必考）
      
      ### Q1: JavaScript有哪些数据类型？
      
      **基本类型**（7种）：
      - String
      - Number
      - Boolean
      - Null
      - Undefined
      - Symbol（ES6）
      - BigInt（ES11）
      
      **引用类型**：Object（包括Array、Function、Date、RegExp等）
      
      **考点**：typeof null为什么返回"object"？
      - 这是JavaScript的历史遗留bug
      - 在底层，null被表示为全0，而对象的类型标签也是0
      - 正确判断null：\`value === null\`
      
      ### Q2: 如何判断数据类型？
      
      \`\`\`javascript
      // 1. typeof - 适合基本类型
      typeof 'hello'          // 'string'
      typeof 123             // 'number'
      typeof true            // 'boolean'
      typeof undefined       // 'undefined'
      typeof Symbol()        // 'symbol'
      typeof null            // 'object' ⚠️
      
      // 2. instanceof - 适合引用类型
      [] instanceof Array               // true
      ({}) instanceof Object            // true
      function(){} instanceof Function  // true
      
      // 3. Object.prototype.toString - 最准确
      Object.prototype.toString.call([])        // '[object Array]'
      Object.prototype.toString.call({})        // '[object Object]'
      Object.prototype.toString.call(null)      // '[object Null]'
      Object.prototype.toString.call(undefined) // '[object Undefined]'
      
      // 4. Array.isArray - 专门判断数组
      Array.isArray([])  // true
      \`\`\`
      
      ### Q3: null和undefined的区别？
      
      \`\`\`javascript
      // undefined：变量声明了但未赋值
      let a;
      console.log(a);  // undefined
      
      // null：表示空对象，需要手动赋值
      let b = null;
      
      // 实际使用：
      // undefined - 函数参数没传、对象属性不存在
      function foo(x) {
        console.log(x);  // undefined
      }
      foo();
      
      // null - 主动清空对象引用
      let obj = { name: 'John' };
      obj = null;  // 等待垃圾回收
      \`\`\`
      
      ## 二、原型和原型链（高频）
      
      ### Q4: 什么是原型链？
      
      \`\`\`javascript
      function Person(name) {
        this.name = name;
      }
      
      Person.prototype.sayName = function() {
        console.log(this.name);
      };
      
      const p = new Person('John');
      
      // 原型链：
      p.__proto__ === Person.prototype                    // true
      Person.prototype.__proto__ === Object.prototype     // true
      Object.prototype.__proto__ === null                 // true
      
      // 查找过程：
      p.sayName()  // 1. 找p自身 → 2. 找Person.prototype → 3. 找Object.prototype
      \`\`\`
      
      **考点**：
      - 实例的 \_\_proto\_\_ 指向构造函数的 prototype
      - prototype 也是对象，所以也有 \_\_proto\_\_
      - 最终都指向 Object.prototype
      - Object.prototype.\_\_proto\_\_ 是 null（终点）
      
      ### Q5: 如何实现继承？
      
      \`\`\`javascript
      // ES6 Class（推荐）
      class Animal {
        constructor(name) {
          this.name = name;
        }
        
        speak() {
          console.log(\`\${this.name} makes a sound\`);
        }
      }
      
      class Dog extends Animal {
        constructor(name, breed) {
          super(name);
          this.breed = breed;
        }
        
        speak() {
          console.log(\`\${this.name} barks\`);
        }
      }
      
      // ES5 原型链继承
      function Animal(name) {
        this.name = name;
      }
      
      Animal.prototype.speak = function() {
        console.log(this.name + ' makes a sound');
      };
      
      function Dog(name, breed) {
        Animal.call(this, name);  // 继承属性
        this.breed = breed;
      }
      
      Dog.prototype = Object.create(Animal.prototype);  // 继承方法
      Dog.prototype.constructor = Dog;
      \`\`\`
      
      ## 三、作用域和闭包（必考）
      
      ### Q6: 什么是闭包？
      
      **定义**：函数能够访问其外部作用域的变量
      
      \`\`\`javascript
      function outer() {
        let count = 0;
        
        return function inner() {
          count++;
          return count;
        };
      }
      
      const counter = outer();
      console.log(counter());  // 1
      console.log(counter());  // 2
      // count在outer执行完后仍然存在，因为inner引用了它
      \`\`\`
      
      **应用场景**：
      1. 数据私有化
      2. 函数柯里化
      3. 模块化
      4. 回调函数
      
      **注意**：闭包会导致内存无法释放，使用完记得清除引用
      
      ### Q7: var、let、const的区别？
      
      \`\`\`javascript
      // 1. 作用域
      var a = 1;   // 函数作用域
      let b = 2;   // 块级作用域
      const c = 3; // 块级作用域
      
      if (true) {
        var a = 10;
        let b = 20;
      }
      console.log(a);  // 10
      console.log(b);  // 报错
      
      // 2. 变量提升
      console.log(a);  // undefined
      var a = 1;
      
      console.log(b);  // 报错（暂时性死区）
      let b = 2;
      
      // 3. 重复声明
      var a = 1;
      var a = 2;   // ✅
      
      let b = 1;
      let b = 2;   // ❌
      
      // 4. const
      const obj = { name: 'John' };
      obj.name = 'Jane';  // ✅ 可以修改对象属性
      obj = {};           // ❌ 不能重新赋值
      \`\`\`
      
      ## 四、this指向（高频）
      
      ### Q8: this的指向规则？
      
      \`\`\`javascript
      // 1. 默认绑定：全局对象
      function foo() {
        console.log(this);
      }
      foo();  // window（浏览器）
      
      // 2. 隐式绑定：调用者
      const obj = {
        name: 'John',
        sayName: function() {
          console.log(this.name);
        }
      };
      obj.sayName();  // 'John'
      
      // 隐式绑定丢失
      const fn = obj.sayName;
      fn();  // undefined（this变成了全局对象）
      
      // 3. 显式绑定：call/apply/bind
      fn.call(obj);    // 'John'
      fn.apply(obj);   // 'John'
      const bound = fn.bind(obj);
      bound();         // 'John'
      
      // 4. new绑定
      function Person(name) {
        this.name = name;
      }
      const p = new Person('John');  // this指向新创建的对象
      
      // 5. 箭头函数：继承外层this
      const obj2 = {
        name: 'John',
        sayName: () => {
          console.log(this.name);
        }
      };
      obj2.sayName();  // undefined（继承外层，即全局）
      \`\`\`
      
      **优先级**：new > call/apply/bind > 对象方法 > 全局
      
      ## 五、异步编程（重点）
      
      ### Q9: Promise的使用和原理？
      
      \`\`\`javascript
      // 基本使用
      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('success');
        }, 1000);
      });
      
      promise.then(result => {
        console.log(result);
      }).catch(error => {
        console.error(error);
      });
      
      // 链式调用
      fetch('/api/user')
        .then(res => res.json())
        .then(data => {
          console.log(data);
          return fetch(\`/api/posts/\${data.id}\`);
        })
        .then(res => res.json())
        .then(posts => {
          console.log(posts);
        })
        .catch(error => {
          console.error(error);
        });
      
      // Promise.all - 并行
      const [user, posts] = await Promise.all([
        fetch('/api/user'),
        fetch('/api/posts')
      ]);
      
      // Promise.race - 竞速
      const result = await Promise.race([
        fetch('/api/slow'),
        new Promise((_, reject) => 
          setTimeout(() => reject('timeout'), 3000)
        )
      ]);
      \`\`\`
      
      ### Q10: async/await的原理？
      
      \`\`\`javascript
      // async函数返回Promise
      async function getData() {
        return 'data';  // 相当于 return Promise.resolve('data')
      }
      
      // await等待Promise完成
      async function fetchUser() {
        try {
          const response = await fetch('/api/user');
          const data = await response.json();
          return data;
        } catch (error) {
          console.error(error);
        }
      }
      
      // 实际上是Generator + Promise的语法糖
      // 等价于：
      function* getData() {
        const response = yield fetch('/api/user');
        const data = yield response.json();
        return data;
      }
      \`\`\`
      
      ### Q11: 如何实现并发控制？
      
      \`\`\`javascript
      // 限制同时最多3个请求
      async function asyncPool(poolLimit, array, iteratorFn) {
        const result = [];
        const executing = [];
        
        for (const item of array) {
          const p = Promise.resolve().then(() => iteratorFn(item));
          result.push(p);
          
          if (poolLimit <= array.length) {
            const e = p.then(() => executing.splice(executing.indexOf(e), 1));
            executing.push(e);
            
            if (executing.length >= poolLimit) {
              await Promise.race(executing);
            }
          }
        }
        
        return Promise.all(result);
      }
      
      // 使用
      const urls = [/* 100个url */];
      asyncPool(3, urls, url => fetch(url));
      \`\`\`
      
      ## 六、数组方法（常考）
      
      ### Q12: map、forEach、filter、reduce的区别？
      
      \`\`\`javascript
      const arr = [1, 2, 3, 4, 5];
      
      // forEach - 遍历，无返回值
      arr.forEach(item => console.log(item));
      
      // map - 映射，返回新数组
      const doubled = arr.map(item => item * 2);  // [2, 4, 6, 8, 10]
      
      // filter - 过滤，返回新数组
      const filtered = arr.filter(item => item > 2);  // [3, 4, 5]
      
      // reduce - 归约，返回单个值
      const sum = arr.reduce((acc, item) => acc + item, 0);  // 15
      
      // 区别：
      // forEach：不能break，不返回新数组
      // map：必须返回值，生成新数组
      // filter：根据条件过滤
      // reduce：累积计算
      \`\`\`
      
      ### Q13: 数组去重的方法？
      
      \`\`\`javascript
      const arr = [1, 2, 2, 3, 3, 4];
      
      // 方法1：Set（最简单）
      [...new Set(arr)]  // [1, 2, 3, 4]
      
      // 方法2：filter + indexOf
      arr.filter((item, index) => arr.indexOf(item) === index)
      
      // 方法3：reduce
      arr.reduce((acc, item) => 
        acc.includes(item) ? acc : [...acc, item], 
      []）
      
      // 方法4：对象去重（适合对象数组）
      const arr2 = [
        { id: 1, name: 'a' },
        { id: 2, name: 'b' },
        { id: 1, name: 'c' }
      ];
      
      const unique = Object.values(
        arr2.reduce((acc, item) => {
          acc[item.id] = item;
          return acc;
        }, {})
      );  // [{ id: 1, name: 'c' }, { id: 2, name: 'b' }]
      \`\`\`
      
      ## 七、手写代码（重点）
      
      ### Q14: 手写防抖
      
      \`\`\`javascript
      function debounce(fn, delay) {
        let timer = null;
        
        return function(...args) {
          clearTimeout(timer);
          
          timer = setTimeout(() => {
            fn.apply(this, args);
          }, delay);
        };
      }
      
      // 使用
      input.addEventListener('input', debounce(function(e) {
        console.log(e.target.value);
      }, 300));
      \`\`\`
      
      ### Q15: 手写节流
      
      \`\`\`javascript
      function throttle(fn, delay) {
        let lastTime = 0;
        
        return function(...args) {
          const now = Date.now();
          
          if (now - lastTime >= delay) {
            fn.apply(this, args);
            lastTime = now;
          }
        };
      }
      
      // 使用
      window.addEventListener('scroll', throttle(function() {
        console.log('scrolling');
      }, 200));
      \`\`\`
      
      ### Q16: 手写深拷贝
      
      \`\`\`javascript
      function deepClone(obj, map = new WeakMap()) {
        // 基本类型直接返回
        if (obj === null || typeof obj !== 'object') {
          return obj;
        }
        
        // 处理循环引用
        if (map.has(obj)) {
          return map.get(obj);
        }
        
        // 处理日期
        if (obj instanceof Date) {
          return new Date(obj);
        }
        
        // 处理正则
        if (obj instanceof RegExp) {
          return new RegExp(obj);
        }
        
        // 处理数组
        if (Array.isArray(obj)) {
          const cloned = [];
          map.set(obj, cloned);
          obj.forEach(item => {
            cloned.push(deepClone(item, map));
          });
          return cloned;
        }
        
        // 处理对象
        const cloned = {};
        map.set(obj, cloned);
        
        Object.keys(obj).forEach(key => {
          cloned[key] = deepClone(obj[key], map);
        });
        
        return cloned;
      }
      \`\`\`
      
      ### Q17: 手写Promise.all
      
      \`\`\`javascript
      Promise.myAll = function(promises) {
        return new Promise((resolve, reject) => {
          if (!Array.isArray(promises)) {
            return reject(new TypeError('arguments must be an array'));
          }
          
          const result = [];
          let count = 0;
          
          promises.forEach((promise, index) => {
            Promise.resolve(promise).then(
              value => {
                result[index] = value;
                count++;
                
                if (count === promises.length) {
                  resolve(result);
                }
              },
              reason => {
                reject(reason);
              }
            );
          });
        });
      };
      \`\`\`
      
      ## 总结
      
      这些是JavaScript基础部分的高频考题，建议：
      1. 理解原理，不要死记硬背
      2. 多动手写代码
      3. 结合实际项目理解
      4. 准备好每道题的答题思路
      
      下一篇会讲ES6+的面试题，包括Promise、async/await、模块化等。
      `
      },
      {
        title: '2025前端面试题汇总：ES6+特性详解与实战',
        slug: '2025-frontend-interview-es6-features',
        excerpt: 'ES6+是面试必考内容，整理了Promise、async/await、模块化、Proxy等核心特性的常见问题。',
        category: '面试准备',
        tags: ['面试', 'ES6', '前端'],
        status: 'published',
        isFeatured: true,
        views: 7650,
        likes: 634,
        publishedAt: new Date('2025-09-03'),
        content: `
      ES6+的特性是现在面试的必考内容，这篇整理了高频问题。
      
      ## 一、解构赋值
      
      ### Q1: 解构赋值的常见用法？
      
      \`\`\`javascript
      // 数组解构
      const [a, b, c] = [1, 2, 3];
      const [first, ...rest] = [1, 2, 3, 4];  // rest = [2, 3, 4]
      
      // 默认值
      const [x = 0] = [];  // x = 0
      
      // 交换变量
      let a = 1, b = 2;
      [a, b] = [b, a];
      
      // 对象解构
      const { name, age } = { name: 'John', age: 25 };
      
      // 重命名
      const { name: userName } = { name: 'John' };
      
      // 嵌套解构
      const user = {
        info: {
          name: 'John',
          address: { city: 'Beijing' }
        }
      };
      const { info: { address: { city } } } = user;
      
      // 函数参数解构
      function greet({ name, age = 18 }) {
        console.log(\`\${name}, \${age}\`);
      }
      \`\`\`
      
      ## 二、箭头函数
      
      ### Q2: 箭头函数和普通函数的区别？
      
      \`\`\`javascript
      // 1. this指向不同
      const obj = {
        name: 'John',
        sayName: function() {
          console.log(this.name);  // 'John'
        },
        sayName2: () => {
          console.log(this.name);  // undefined（继承外层this）
        }
      };
      
      // 2. 不能作为构造函数
      const Person = (name) => {
        this.name = name;
      };
      new Person('John');  // ❌ TypeError
      
      // 3. 没有arguments
      const foo = () => {
        console.log(arguments);  // ❌ ReferenceError
      };
      
      // 用rest参数代替
      const foo2 = (...args) => {
        console.log(args);  // ✅
      };
      
      // 4. 不能用作Generator函数
      const foo3 = *() => {  // ❌ 语法错误
        yield 1;
      };
      \`\`\`
      
      **何时使用箭头函数**：
      - 需要继承外层this时
      - 简短的回调函数
      - 不需要arguments时
      
      **不使用箭头函数**：
      - 对象方法
      - 构造函数
      - 需要动态this时
      
      ## 三、Promise（重点）
      
      ### Q3: Promise有几种状态？
      
      **三种状态**：
      - pending（进行中）
      - fulfilled（已成功）
      - rejected（已失败）
      
      **特点**：
      - 状态只能从pending → fulfilled或pending → rejected
      - 状态一旦改变就不会再变
      - 无法取消Promise
      
      ### Q4: Promise的常用方法？
      
      \`\`\`javascript
      // 1. Promise.resolve/reject
      Promise.resolve('success');
      Promise.reject('error');
      
      // 2. Promise.all - 全部成功才成功
      Promise.all([p1, p2, p3])
        .then(results => {
          // results = [r1, r2, r3]
        })
        .catch(error => {
          // 任何一个失败都会走这里
        });
      
      // 3. Promise.race - 最快的那个
      Promise.race([p1, p2, p3])
        .then(result => {
          // 最先完成的结果
        });
      
      // 4. Promise.allSettled - 等待全部完成（ES2020）
      Promise.allSettled([p1, p2, p3])
        .then(results => {
          // results = [
          //   { status: 'fulfilled', value: r1 },
          //   { status: 'rejected', reason: e2 },
          //   { status: 'fulfilled', value: r3 }
          // ]
        });
      
      // 5. Promise.any - 任意一个成功（ES2021）
      Promise.any([p1, p2, p3])
        .then(result => {
          // 第一个成功的结果
        })
        .catch(error => {
          // 全部失败才会走这里
        });
      \`\`\`
      
      ### Q5: 如何串行执行Promise？
      
      \`\`\`javascript
      const urls = ['url1', 'url2', 'url3'];
      
      // 方法1：reduce
      urls.reduce((promise, url) => {
        return promise.then(() => fetch(url));
      }, Promise.resolve());
      
      // 方法2：async/await + for循环
      async function fetchSequentially(urls) {
        for (const url of urls) {
          await fetch(url);
        }
      }
      
      // 方法3：递归
      async function fetchSequentially(urls) {
        if (urls.length === 0) return;
        
        await fetch(urls[0]);
        await fetchSequentially(urls.slice(1));
      }
      \`\`\`
      
      ## 四、async/await
      
      ### Q6: async/await的错误处理？
      
      \`\`\`javascript
      // 方法1：try-catch
      async function getData() {
        try {
          const data = await fetch('/api/data');
          return data.json();
        } catch (error) {
          console.error(error);
          return null;
        }
      }
      
      // 方法2：统一错误处理
      async function getData() {
        return fetch('/api/data')
          .then(res => res.json())
          .catch(error => {
            console.error(error);
            return null;
          });
      }
      
      // 方法3：封装工具函数
      function to(promise) {
        return promise
          .then(data => [null, data])
          .catch(err => [err, null]);
      }
      
      const [error, data] = await to(fetch('/api/data'));
      if (error) {
        // 处理错误
      }
      \`\`\`
      
      ### Q7: async/await和Promise的区别？
      
      \`\`\`javascript
      // Promise链式调用
      fetch('/api/user')
        .then(res => res.json())
        .then(user => fetch(\`/api/posts/\${user.id}\`))
        .then(res => res.json())
        .then(posts => {
          console.log(posts);
        })
        .catch(error => {
          console.error(error);
        });
      
      // async/await更直观
      async function getPosts() {
        try {
          const userRes = await fetch('/api/user');
          const user = await userRes.json();
          
          const postsRes = await fetch(\`/api/posts/\${user.id}\`);
          const posts = await postsRes.json();
          
          console.log(posts);
        } catch (error) {
          console.error(error);
        }
      }
      \`\`\`
      
      **区别**：
      - async/await是Promise的语法糖
      - async/await让异步代码看起来像同步
      - 错误处理：Promise用catch，async/await用try-catch
      - async/await更适合复杂的异步流程
      
      ## 五、模块化
      
      ### Q8: ES6模块和CommonJS的区别？
      
      \`\`\`javascript
      // ES6模块
      // a.js
      export const name = 'John';
      export function greet() {}
      export default class Person {}
      
      // b.js
      import Person, { name, greet } from './a.js';
      
      // CommonJS
      // a.js
      module.exports = {
        name: 'John',
        greet() {}
      };
      
      // b.js
      const { name, greet } = require('./a.js');
      \`\`\`
      
      **主要区别**：
      1. **语法**：ES6用import/export，CommonJS用require/module.exports
      2. **加载时机**：ES6编译时加载，CommonJS运行时加载
      3. **输出**：ES6输出的是值的引用，CommonJS输出的是值的拷贝
      4. **this**：ES6模块中this是undefined，CommonJS中是当前模块
      5. **循环依赖**：ES6可以处理，CommonJS有问题
      
      ## 六、Class
      
      ### Q9: Class的继承原理？
      
      \`\`\`javascript
      class Animal {
        constructor(name) {
          this.name = name;
        }
        
        speak() {
          console.log(this.name + ' makes a sound');
        }
      }
      
      class Dog extends Animal {
        constructor(name, breed) {
          super(name);  // 必须先调用super
          this.breed = breed;
        }
        
        speak() {
          super.speak();  // 调用父类方法
          console.log(this.name + ' barks');
        }
      }
      \`\`\`
      
      **等价于ES5**：
      \`\`\`javascript
      function Animal(name) {
        this.name = name;
      }
      
      Animal.prototype.speak = function() {
        console.log(this.name + ' makes a sound');
      };
      
      function Dog(name, breed) {
        Animal.call(this, name);
        this.breed = breed;
      }
      
      Dog.prototype = Object.create(Animal.prototype);
      Dog.prototype.constructor = Dog;
      \`\`\`
      
      ## 七、Proxy和Reflect
      
      ### Q10: Proxy的应用场景？
      
      \`\`\`javascript
      // 1. 数据验证
      const validator = {
        set(target, key, value) {
          if (key === 'age') {
            if (typeof value !== 'number' || value < 0) {
              throw new TypeError('Age must be a positive number');
            }
          }
          target[key] = value;
          return true;
        }
      };
      
      const person = new Proxy({}, validator);
      person.age = 25;   // ✅
      person.age = -1;   // ❌ TypeError
      
      // 2. 数据绑定（Vue 3响应式原理）
      function reactive(obj) {
        return new Proxy(obj, {
          get(target, key) {
            // 依赖收集
            track(target, key);
            return target[key];
          },
          set(target, key, value) {
            target[key] = value;
            // 触发更新
            trigger(target, key);
            return true;
          }
        });
      }
      
      // 3. 私有属性
      const handler = {
        get(target, key) {
          if (key.startsWith('_')) {
            throw new Error('Private property');
          }
          return target[key];
        }
      };
      
      const obj = new Proxy({ _private: 'secret' }, handler);
      console.log(obj._private);  // ❌ Error
      \`\`\`
      
      ## 八、新特性
      
      ### Q11: 可选链和空值合并？
      
      \`\`\`javascript
      // 可选链 ?.（ES2020）
      const user = {
        info: {
          name: 'John'
        }
      };
      
      // 传统写法
      const city = user && user.info && user.info.address && user.info.address.city;
      
      // 可选链
      const city = user?.info?.address?.city;  // undefined（不报错）
      
      // 函数调用
      obj.method?.();
      
      // 数组
      arr?.[0]
      
      // 空值合并 ??（ES2020）
      const value = null ?? 'default';  // 'default'
      const value2 = 0 ?? 'default';    // 0（只有null/undefined才用默认值）
      
      // 与 || 的区别
      const value3 = 0 || 'default';    // 'default'（0被认为是false）
      const value4 = 0 ?? 'default';    // 0
      \`\`\`
      
      ### Q12: 其他常用新特性？
      
      \`\`\`javascript
      // 1. BigInt（ES2020）
      const big = 9007199254740991n;
      const big2 = BigInt(9007199254740991);
      
      // 2. Promise.finally（ES2018）
      fetch('/api')
        .then(res => res.json())
        .catch(error => console.error(error))
        .finally(() => {
          // 无论成功失败都执行
          hideLoading();
        });
      
      // 3. Object.fromEntries（ES2019）
      const entries = [['name', 'John'], ['age', 25]];
      const obj = Object.fromEntries(entries);  // { name: 'John', age: 25 }
      
      // 4. String.prototype.matchAll（ES2020）
      const str = 'test1test2';
      const matches = [...str.matchAll(/test\\d/g)];
      
      // 5. 数值分隔符（ES2021）
      const num = 1_000_000;  // 1000000
      
      // 6. 逻辑赋值（ES2021）
      a ||= b;  // a || (a = b)
      a &&= b;  // a && (a = b)
      a ??= b;  // a ?? (a = b)
      \`\`\`
      
      ## 总结
      
      ES6+的特性很多，面试重点：
      1. Promise和async/await（必考）
      2. 模块化（ES6 vs CommonJS）
      3. 箭头函数的this
      4. Proxy和Reflect
      5. Class继承
      6. 新特性（可选链、空值合并等）
      
      建议结合实际项目，理解这些特性解决了什么问题。
      
      下一篇会讲React/Vue框架相关的面试题。
      `
      },
      {
        title: '2025前端面试题汇总：React核心原理与常见问题',
        slug: '2025-frontend-interview-react',
        excerpt: 'React面试高频题整理，包括Hooks、虚拟DOM、性能优化、状态管理等核心知识点。',
        category: '面试准备',
        tags: ['面试', 'React', '前端'],
        status: 'published',
        isFeatured: true,
        views: 9340,
        likes: 812,
        publishedAt: new Date('2025-09-01'),
        content: `
      React是前端面试的重点，整理了最常见的问题和答案。
      
      ## 一、React基础
      
      ### Q1: React的核心特点？
      
      **1. 声明式编程**
      \`\`\`jsx
      // 不需要手动操作DOM
      function Counter() {
        const [count, setCount] = useState(0);
        return <div>{count}</div>;
      }
      \`\`\`
      
      **2. 组件化**
      - 复用性强
      - 易于维护
      - 单向数据流
      
      **3. 虚拟DOM**
      - 减少真实DOM操作
      - 批量更新
      - 提高性能
      
      **4. JSX语法**
      - JavaScript + XML
      - 更直观的UI描述
      
      ### Q2: 虚拟DOM的原理？
      
      \`\`\`javascript
      // 1. 用JS对象描述DOM结构
      const vdom = {
        type: 'div',
        props: {
          className: 'container'
        },
        children: [
          {
            type: 'h1',
            children: 'Hello'
          }
        ]
      };
      
      // 2. diff算法比较新旧虚拟DOM
      function diff(oldVdom, newVdom) {
        // 比较差异，生成补丁
      }
      
      // 3. 只更新有变化的部分
      function patch(dom, patches) {
        // 应用补丁到真实DOM
      }
      \`\`\`
      
      **为什么要用虚拟DOM？**
      - 真实DOM操作慢，虚拟DOM操作快
      - 可以批量更新，减少重排重绘
      - 跨平台（React Native）
      
      ## 二、组件
      
      ### Q3: 函数组件和类组件的区别？
      
      \`\`\`jsx
      // 类组件
      class Welcome extends React.Component {
        state = { count: 0 };
        
        componentDidMount() {
          console.log('mounted');
        }
        
        render() {
          return <div>{this.state.count}</div>;
        }
      }
      
      // 函数组件 + Hooks
      function Welcome() {
        const [count, setCount] = useState(0);
        
        useEffect(() => {
          console.log('mounted');
        }, []);
        
        return <div>{count}</div>;
      }
      \`\`\`
      
      **区别**：
      - 类组件有this，函数组件没有
      - 类组件有生命周期，函数组件用Hooks
      - 函数组件代码更简洁
      - 函数组件性能更好（无this绑定）
      
      **React推荐函数组件**
      
      ### Q4: 受控组件和非受控组件？
      
      \`\`\`jsx
      // 受控组件：值由React控制
      function Controlled() {
        const [value, setValue] = useState('');
        
        return (
          <input 
            value={value} 
            onChange={e => setValue(e.target.value)} 
          />
        );
      }
      
      // 非受控组件：值由DOM控制
      function Uncontrolled() {
        const inputRef = useRef();
        
        const handleSubmit = () => {
          console.log(inputRef.current.value);
        };
        
        return <input ref={inputRef} />;
      }
      \`\`\`
      
      **使用建议**：
      - 大部分情况用受控组件
      - 文件上传用非受控组件
      - 需要与第三方库集成时用非受控组件
      
      ## 三、Hooks（重点）
      
      ### Q5: useState的原理？
      
      \`\`\`javascript
      // 简化版实现
      let state = [];
      let index = 0;
      
      function useState(initialValue) {
        const currentIndex = index;
        state[currentIndex] = state[currentIndex] || initialValue;
        
        const setState = (newValue) => {
          state[currentIndex] = newValue;
          render(); // 触发重新渲染
        };
        
        index++;
        return [state[currentIndex], setState];
      }
      
      function Component() {
        const [count, setCount] = useState(0);
        const [name, setName] = useState('');
        
        index = 0; // 重置index
        return <div>{count}</div>;
      }
      \`\`\`
      
      **关键点**：
      - 闭包保存状态
      - 调用顺序必须一致（不能在条件语句中使用）
      - setState可以传函数
      
      \`\`\`jsx
      // ❌ 错误：Hooks顺序变化
      function Bad({ condition }) {
        if (condition) {
          const [count, setCount] = useState(0);
        }
        const [name, setName] = useState('');
      }
      
      // ✅ 正确
      function Good({ condition }) {
        const [count, setCount] = useState(0);
        const [name, setName] = useState('');
      }
      \`\`\`
      
      ### Q6: useEffect的使用？
      
      \`\`\`jsx
      // 1. 基础用法
      useEffect(() => {
        console.log('mounted or updated');
      });
      
      // 2. 只在mount时执行
      useEffect(() => {
        console.log('mounted');
      }, []);
      
      // 3. 依赖某个值
      useEffect(() => {
        console.log('count changed');
      }, [count]);
      
      // 4. 清理函数
      useEffect(() => {
        const timer = setInterval(() => {
          console.log('tick');
        }, 1000);
        
        return () => {
          clearInterval(timer); // 清理
        };
      }, []);
      \`\`\`
      
      **常见问题**：
      1. 依赖数组为空 vs 不传：
         - \`[]\` - 只执行一次
         - 不传 - 每次渲染都执行
      
      2. 如何模拟生命周期：
      \`\`\`jsx
      // componentDidMount
      useEffect(() => {}, []);
      
      // componentDidUpdate
      useEffect(() => {});
      
      // componentWillUnmount
      useEffect(() => {
        return () => {
          // cleanup
        };
      }, []);
      \`\`\`
      
      ### Q7: useMemo和useCallback的区别？
      
      \`\`\`jsx
      // useMemo：缓存计算结果
      const expensiveValue = useMemo(() => {
        return computeExpensiveValue(a, b);
      }, [a, b]);
      
      // useCallback：缓存函数
      const handleClick = useCallback(() => {
        console.log('clicked');
      }, []);
      
      // 等价于
      const handleClick = useMemo(() => {
        return () => console.log('clicked');
      }, []);
      \`\`\`
      
      **使用场景**：
      - useMemo：避免重复计算
      - useCallback：传递给子组件的函数，配合React.memo使用
      
      \`\`\`jsx
      // 子组件用了React.memo，父组件的函数要用useCallback
      const Child = React.memo(({ onClick }) => {
        console.log('Child render');
        return <button onClick={onClick}>Click</button>;
      });
      
      function Parent() {
        const [count, setCount] = useState(0);
        
        // ❌ 每次渲染都创建新函数，Child会重新渲染
        const handleClick = () => {
          console.log('clicked');
        };
        
        // ✅ 函数引用不变，Child不会重新渲染
        const handleClick = useCallback(() => {
          console.log('clicked');
        }, []);
        
        return <Child onClick={handleClick} />;
      }
      \`\`\`
      
      ### Q8: 自定义Hook的应用？
      
      \`\`\`jsx
      // 1. 封装逻辑复用
      function useWindowSize() {
        const [size, setSize] = useState({
          width: window.innerWidth,
          height: window.innerHeight
        });
        
        useEffect(() => {
          const handleResize = () => {
            setSize({
              width: window.innerWidth,
              height: window.innerHeight
            });
          };
          
          window.addEventListener('resize', handleResize);
          return () => window.removeEventListener('resize', handleResize);
        }, []);
        
        return size;
      }
      
      // 使用
      function Component() {
        const { width, height } = useWindowSize();
        return <div>{width} x {height}</div>;
      }
      
      // 2. 封装异步请求
      function useRequest(url) {
        const [data, setData] = useState(null);
        const [loading, setLoading] = useState(true);
        const [error, setError] = useState(null);
        
        useEffect(() => {
          fetch(url)
            .then(res => res.json())
            .then(data => {
              setData(data);
              setLoading(false);
            })
            .catch(err => {
              setError(err);
              setLoading(false);
            });
        }, [url]);
        
        return { data, loading, error };
      }
      \`\`\`
      
      ## 四、性能优化
      
      ### Q9: React性能优化的方法？
      
      \`\`\`jsx
      // 1. React.memo - 避免无效渲染
      const Child = React.memo(({ name }) => {
        console.log('Child render');
        return <div>{name}</div>;
      });
      
      // 2. useMemo - 缓存计算结果
      const value = useMemo(() => {
        return expensiveCalculation(a, b);
      }, [a, b]);
      
      // 3. useCallback - 缓存函数
      const handleClick = useCallback(() => {
        doSomething();
      }, []);
      
      // 4. 懒加载
      const LazyComponent = React.lazy(() => import('./Component'));
      
      function App() {
        return (
          <Suspense fallback={<Loading />}>
            <LazyComponent />
          </Suspense>
        );
      }
      
      // 5. 虚拟列表
      import { FixedSizeList } from 'react-window';
      
      <FixedSizeList
        height={600}
        itemCount={1000}
        itemSize={35}
      >
        {Row}
      </FixedSizeList>
      
      // 6. key的正确使用
      // ❌ 不要用index
      items.map((item, index) => <div key={index}>{item}</div>)
      
      // ✅ 用唯一ID
      items.map(item => <div key={item.id}>{item}</div>)
      
      // 7. 避免在render中创建对象/数组
      // ❌
      <Child style={{ color: 'red' }} />
      
      // ✅
      const style = { color: 'red' };
      <Child style={style} />
      \`\`\`
      
      ### Q10: 什么时候会导致重新渲染？
      
      1. **state变化**
      2. **props变化**
      3. **父组件渲染**
      4. **Context变化**
      5. **forceUpdate调用**
      
      \`\`\`jsx
      // 父组件渲染，子组件也会渲染
      function Parent() {
        const [count, setCount] = useState(0);
        
        return (
          <>
            <button onClick={() => setCount(count + 1)}>Count: {count}</button>
            <Child /> {/* 即使props没变，也会重新渲染 */}
          </>
        );
      }
      
      // 解决：用React.memo
      const Child = React.memo(() => {
        console.log('Child render');
        return <div>Child</div>;
      });
      \`\`\`
      
      ## 五、状态管理
      
      ### Q11: Context的使用和问题？
      
      \`\`\`jsx
      // 创建Context
      const ThemeContext = React.createContext('light');
      
      // 提供值
      function App() {
        const [theme, setTheme] = useState('light');
        
        return (
          <ThemeContext.Provider value={{ theme, setTheme }}>
            <Child />
          </ThemeContext.Provider>
        );
      }
      
      // 消费值
      function Child() {
        const { theme } = useContext(ThemeContext);
        return <div>Theme: {theme}</div>;
      }
      \`\`\`
      
      **Context的问题**：
      - 所有消费者都会重新渲染
      - 不适合频繁更新的数据
      
      **优化方案**：
      \`\`\`jsx
      // 拆分Context
      const ThemeContext = React.createContext();
      const ThemeUpdateContext = React.createContext();
      
      function ThemeProvider({ children }) {
        const [theme, setTheme] = useState('light');
        
        return (
          <ThemeContext.Provider value={theme}>
            <ThemeUpdateContext.Provider value={setTheme}>
              {children}
            </ThemeUpdateContext.Provider>
          </ThemeContext.Provider>
        );
      }
      
      // 只需要theme的组件
      function DisplayTheme() {
        const theme = useContext(ThemeContext);
        return <div>{theme}</div>;
      }
      
      // 只需要setTheme的组件
      function ToggleTheme() {
        const setTheme = useContext(ThemeUpdateContext);
        return <button onClick={() => setTheme('dark')}>Toggle</button>;
      }
      \`\`\`
      
      ### Q12: Redux的核心概念？
      
      \`\`\`javascript
      // Store：单一数据源
      const store = createStore(reducer);
      
      // Action：描述发生了什么
      const action = {
        type: 'INCREMENT',
        payload: 1
      };
      
      // Reducer：根据action更新state
      function reducer(state = { count: 0 }, action) {
        switch (action.type) {
          case 'INCREMENT':
            return { count: state.count + action.payload };
          default:
            return state;
        }
      }
      
      // Dispatch：派发action
      store.dispatch({ type: 'INCREMENT', payload: 1 });
      
      // 使用
      import { useSelector, useDispatch } from 'react-redux';
      
      function Counter() {
        const count = useSelector(state => state.count);
        const dispatch = useDispatch();
        
        return (
          <>
            <div>{count}</div>
            <button onClick={() => dispatch({ type: 'INCREMENT', payload: 1 })}>
              +1
            </button>
          </>
        );
      }
      \`\`\`
      
      **Redux工作流程**：
      1. 组件dispatch一个action
      2. Reducer根据action计算新的state
      3. Store更新
      4. 组件重新渲染
      
      ## 六、常见问题
      
      ### Q13: React中的key有什么作用？
      
      \`\`\`jsx
      // key帮助React识别哪些元素改变了
      const items = ['A', 'B', 'C'];
      
      // ❌ 用index做key
      items.map((item, index) => <div key={index}>{item}</div>)
      
      // 问题：删除'B'后，'C'的key从2变成1，React认为'C'被修改了
      // ['A', 'B', 'C'] -> ['A', 'C']
      // key:  0    1    2  ->  0    1
      
      // ✅ 用唯一ID做key
      items.map(item => <div key={item.id}>{item}</div>)
      \`\`\`
      
      **key的作用**：
      - 帮助React识别元素
      - 优化列表渲染
      - 避免不必要的DOM操作
      
      ### Q14: 为什么不能在条件语句中使用Hooks？
      
      \`\`\`jsx
      // ❌ 错误
      function Bad({ condition }) {
        if (condition) {
          const [count, setCount] = useState(0); // 顺序不固定
        }
        const [name, setName] = useState('');
      }
      
      // React内部用数组存储Hooks
      // 第一次渲染：[count, name]
      // condition变false后：[name]
      // 数组索引对不上了！
      \`\`\`
      
      **原因**：
      - React用调用顺序来记录Hooks
      - 顺序变化会导致state错乱
      
      ### Q15: React的合成事件？
      
      \`\`\`jsx
      function Button() {
        const handleClick = (e) => {
          e.preventDefault();      // 合成事件
          e.stopPropagation();     // 合成事件
          
          console.log(e.nativeEvent); // 原生事件
        };
        
        return <button onClick={handleClick}>Click</button>;
      }
      \`\`\`
      
      **合成事件的优点**：
      - 跨浏览器兼容
      - 事件委托，性能更好
      - React统一管理
      
      **注意**：
      - 异步访问事件对象需要调用e.persist()
      - 或者提前保存需要的值
      
      ## 总结
      
      React面试重点：
      1. Hooks的原理和使用（必考）
      2. 虚拟DOM和diff算法
      3. 性能优化方法
      4. 生命周期和副作用
      5. 状态管理方案
      
      建议多做项目，理解React的设计理念。
      
      下一篇是Vue的面试题。
      `
      },
      {
        title: '2025前端面试题汇总：Vue3核心原理与实战',
        slug: '2025-frontend-interview-vue3',
        excerpt: 'Vue3面试高频题整理，包括响应式原理、Composition API、生命周期、性能优化等。',
        category: '面试准备',
        tags: ['面试', 'Vue', 'Vue3'],
        status: 'published',
        isFeatured: true,
        views: 8560,
        likes: 723,
        publishedAt: new Date('2025-08-29'),
        content: `
      Vue3是目前很多公司的技术栈首选，整理了面试中的常见问题。
      
      ## 一、响应式原理
      
      ### Q1: Vue3的响应式原理？
      
      Vue3使用Proxy替代了Vue2的Object.defineProperty：
      
      \`\`\`javascript
      // Vue3响应式实现（简化版）
      function reactive(target) {
        return new Proxy(target, {
          get(target, key, receiver) {
            // 依赖收集
            track(target, key);
            return Reflect.get(target, key, receiver);
          },
          set(target, key, value, receiver) {
            const result = Reflect.set(target, key, value, receiver);
            // 触发更新
            trigger(target, key);
            return result;
          }
        });
      }
      
      const state = reactive({ count: 0 });
      \`\`\`
      
      **Proxy vs Object.defineProperty的优势**：
      - 可以监听数组变化，不需要额外处理
      - 可以监听对象属性的新增和删除
      - 可以监听Map、Set等数据结构
      - 性能更好，懒监听（访问时才代理）
      
      ### Q2: ref和reactive的区别？
      
      \`\`\`javascript
      import { ref, reactive } from 'vue';
      
      // ref - 适合基本类型
      const count = ref(0);
      console.log(count.value); // 需要.value
      count.value++;
      
      // reactive - 适合对象
      const state = reactive({ count: 0 });
      console.log(state.count); // 直接访问
      state.count++;
      \`\`\`
      
      **核心区别**：
      - ref: 返回RefImpl对象，通过.value访问
      - reactive: 返回Proxy对象，直接访问属性
      - ref内部会调用reactive
      - ref可以重新赋值，reactive不能
      
      ## 二、Composition API
      
      ### Q3: Composition API的优势？
      
      **1. 更好的逻辑复用**
      
      \`\`\`javascript
      // 可组合的函数
      function useMouse() {
        const x = ref(0);
        const y = ref(0);
        
        function update(e) {
          x.value = e.pageX;
          y.value = e.pageY;
        }
        
        onMounted(() => {
          window.addEventListener('mousemove', update);
        });
        
        onUnmounted(() => {
          window.removeEventListener('mousemove', update);
        });
        
        return { x, y };
      }
      
      // 在组件中使用
      const { x, y } = useMouse();
      \`\`\`
      
      **2. 更好的TypeScript支持**
      
      \`\`\`typescript
      interface User {
        name: string;
        age: number;
      }
      
      const user = ref<User>({ name: 'Tom', age: 18 });
      // 完整的类型推断
      \`\`\`
      
      **3. 更灵活的代码组织**
      - Options API: 按选项类型组织（data、methods、computed）
      - Composition API: 按功能逻辑组织
      
      ### Q4: watchEffect和watch的区别？
      
      \`\`\`javascript
      import { ref, watch, watchEffect } from 'vue';
      
      const count = ref(0);
      
      // watch - 明确指定监听源
      watch(count, (newVal, oldVal) => {
        console.log('count变化', newVal, oldVal);
      });
      
      // watchEffect - 自动收集依赖
      watchEffect(() => {
        console.log('count:', count.value);
        // 会自动追踪count的变化
      });
      \`\`\`
      
      **区别**：
      - watch需要明确指定监听的数据源
      - watchEffect自动收集依赖，首次会立即执行
      - watch可以获取新旧值，watchEffect不能
      - watch默认是懒执行，watchEffect会立即执行
      
      ## 三、性能优化
      
      ### Q5: Vue3有哪些性能优化？
      
      **1. 静态提升**
      
      \`\`\`javascript
      // 编译前
      <div>
        <span>静态文本</span>
        <span>{{ dynamic }}</span>
      </div>
      
      // 编译后（简化）
      const _hoisted_1 = createVNode("span", null, "静态文本");
      
      function render() {
        return createVNode("div", null, [
          _hoisted_1, // 复用，不重新创建
          createVNode("span", null, ctx.dynamic)
        ]);
      }
      \`\`\`
      
      **2. 事件缓存**
      
      \`\`\`javascript
      <button @click="handleClick">点击</button>
      
      // Vue2每次都创建新函数
      // Vue3会缓存事件处理器
      \`\`\`
      
      **3. Tree-shaking**
      - 按需引入API
      - 打包体积更小
      
      **4. Fragments**
      - 支持多个根节点
      - 减少不必要的DOM层级
      
      ### Q6: v-if和v-show的区别？
      
      \`\`\`vue
      <!-- v-if: 条件渲染 -->
      <div v-if="show">显示</div>
      
      <!-- v-show: 切换display -->
      <div v-show="show">显示</div>
      \`\`\`
      
      **使用场景**：
      - v-if: 切换频率低，初始渲染条件为false（懒加载）
      - v-show: 切换频率高，始终渲染到DOM
      
      ## 四、组件通信
      
      ### Q7: Vue3组件通信方式？
      
      **1. Props / Emits**
      
      \`\`\`javascript
      // 父组件
      <Child :msg="message" @update="handleUpdate" />
      
      // 子组件
      const props = defineProps({
        msg: String
      });
      
      const emit = defineEmits(['update']);
      emit('update', newValue);
      \`\`\`
      
      **2. Provide / Inject**
      
      \`\`\`javascript
      // 祖先组件
      provide('theme', 'dark');
      
      // 后代组件
      const theme = inject('theme');
      \`\`\`
      
      **3. Vuex / Pinia**
      
      \`\`\`javascript
      // Pinia store
      import { defineStore } from 'pinia';
      
      export const useUserStore = defineStore('user', {
        state: () => ({
          name: 'Tom'
        }),
        actions: {
          updateName(name) {
            this.name = name;
          }
        }
      });
      \`\`\`
      
      ## 五、生命周期
      
      ### Q8: Vue3生命周期有哪些变化？
      
      \`\`\`javascript
      // Options API
      export default {
        beforeCreate() {},
        created() {},
        beforeMount() {},
        mounted() {},
        beforeUpdate() {},
        updated() {},
        beforeUnmount() {}, // Vue2是beforeDestroy
        unmounted() {},     // Vue2是destroyed
      }
      
      // Composition API
      import { 
        onBeforeMount,
        onMounted,
        onBeforeUpdate,
        onUpdated,
        onBeforeUnmount,
        onUnmounted
      } from 'vue';
      
      setup() {
        onMounted(() => {
          console.log('mounted');
        });
        
        // setup相当于beforeCreate和created
      }
      \`\`\`
      
      ## 六、实战题目
      
      ### Q9: 实现一个防抖输入框
      
      \`\`\`vue
      <template>
        <input v-model="searchText" placeholder="搜索" />
        <div>搜索结果: {{ results }}</div>
      </template>
      
      <script setup>
      import { ref, watch } from 'vue';
      
      const searchText = ref('');
      const results = ref([]);
      
      // 防抖函数
      function debounce(fn, delay) {
        let timer = null;
        return function(...args) {
          if (timer) clearTimeout(timer);
          timer = setTimeout(() => {
            fn.apply(this, args);
          }, delay);
        };
      }
      
      // 搜索函数
      const search = debounce(async (keyword) => {
        if (!keyword) {
          results.value = [];
          return;
        }
        
        const res = await fetch(\`/api/search?q=\${keyword}\`);
        results.value = await res.json();
      }, 500);
      
      watch(searchText, (newVal) => {
        search(newVal);
      });
      </script>
      \`\`\`
      
      ### Q10: 实现keep-alive的使用
      
      \`\`\`vue
      <template>
        <keep-alive :include="['Home', 'About']">
          <router-view />
        </keep-alive>
      </template>
      
      <script setup>
      import { onActivated, onDeactivated } from 'vue';
      
      // 组件被激活时调用
      onActivated(() => {
        console.log('组件激活');
        // 刷新数据
      });
      
      // 组件被缓存时调用
      onDeactivated(() => {
        console.log('组件缓存');
      });
      </script>
      \`\`\`
      
      ## 总结
      
      Vue3的核心变化：
      - 响应式系统从Object.defineProperty升级到Proxy
      - Composition API提供更好的逻辑复用
      - 性能优化：静态提升、事件缓存、Tree-shaking
      - 更好的TypeScript支持
      - 生命周期钩子命名调整
      
      准备面试时重点关注：
      1. 响应式原理的底层实现
      2. Composition API的使用场景
      3. 性能优化手段
      4. 与Vue2的区别
      5. 实际项目经验
      `
      },
      {
        title: '前端面试手写代码大全：20个必会的JavaScript实现',
        slug: 'frontend-interview-code-implementations',
        excerpt: '整理了前端面试中最常考的手写代码题，包括防抖节流、深拷贝、Promise实现等。',
        category: '面试准备',
        tags: ['面试', '手写代码', 'JavaScript'],
        status: 'published',
        isFeatured: true,
        views: 12340,
        likes: 1089,
        publishedAt: new Date('2025-08-27'),
        content: `
      手写代码是面试的重点，这些都要能写出来。
      
      ## 1. 防抖（debounce）
      
      \`\`\`javascript
      /**
       * 防抖：n秒内只执行一次，如果n秒内再次触发，重新计时
       * 应用：搜索框输入、窗口resize
       */
      function debounce(fn, delay) {
        let timer = null;
        
        return function(...args) {
          // 清除之前的定时器
          clearTimeout(timer);
          
          // 设置新的定时器
          timer = setTimeout(() => {
            fn.apply(this, args);
          }, delay);
        };
      }
      
      // 使用
      const handleInput = debounce(function(e) {
        console.log('搜索:', e.target.value);
      }, 500);
      
      input.addEventListener('input', handleInput);
      \`\`\`
      
      ## 2. 节流（throttle）
      
      \`\`\`javascript
      /**
       * 节流：n秒内只执行一次
       * 应用：滚动事件、鼠标移动
       */
      function throttle(fn, delay) {
        let lastTime = 0;
        
        return function(...args) {
          const now = Date.now();
          
          if (now - lastTime >= delay) {
            fn.apply(this, args);
            lastTime = now;
          }
        };
      }
      
      // 使用
      const handleScroll = throttle(function() {
        console.log('滚动位置:', window.scrollY);
      }, 200);
      
      window.addEventListener('scroll', handleScroll);
      \`\`\`
      
      ## 3. 深拷贝
      
      \`\`\`javascript
      /**
       * 深拷贝：完全复制对象，包括嵌套对象
       */
      function deepClone(obj, map = new WeakMap()) {
        // 基本类型直接返回
        if (obj === null || typeof obj !== 'object') {
          return obj;
        }
        
        // 处理循环引用
        if (map.has(obj)) {
          return map.get(obj);
        }
        
        // 处理特殊对象
        if (obj instanceof Date) return new Date(obj);
        if (obj instanceof RegExp) return new RegExp(obj);
        if (obj instanceof Map) return new Map(obj);
        if (obj instanceof Set) return new Set(obj);
        
        // 创建新对象
        const cloned = Array.isArray(obj) ? [] : {};
        
        // 记录已拷贝对象，防止循环引用
        map.set(obj, cloned);
        
        // 递归拷贝
        Object.keys(obj).forEach(key => {
          cloned[key] = deepClone(obj[key], map);
        });
        
        return cloned;
      }
      
      // 测试
      const obj = {
        name: 'John',
        info: {
          age: 25
        },
        hobbies: ['reading']
      };
      obj.self = obj; // 循环引用
      
      const copied = deepClone(obj);
      console.log(copied === obj); // false
      \`\`\`
      
      ## 4. 实现Promise
      
      \`\`\`javascript
      class MyPromise {
        constructor(executor) {
          this.state = 'pending';
          this.value = undefined;
          this.reason = undefined;
          this.onFulfilledCallbacks = [];
          this.onRejectedCallbacks = [];
          
          const resolve = (value) => {
            if (this.state === 'pending') {
              this.state = 'fulfilled';
              this.value = value;
              this.onFulfilledCallbacks.forEach(fn => fn());
            }
          };
          
          const reject = (reason) => {
            if (this.state === 'pending') {
              this.state = 'rejected';
              this.reason = reason;
              this.onRejectedCallbacks.forEach(fn => fn());
            }
          };
          
          try {
            executor(resolve, reject);
          } catch (error) {
            reject(error);
          }
        }
        
        then(onFulfilled, onRejected) {
          onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
          onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
          
          return new MyPromise((resolve, reject) => {
            if (this.state === 'fulfilled') {
              setTimeout(() => {
                try {
                  const x = onFulfilled(this.value);
                  resolve(x);
                } catch (error) {
                  reject(error);
                }
              });
            }
            
            if (this.state === 'rejected') {
              setTimeout(() => {
                try {
                  const x = onRejected(this.reason);
                  resolve(x);
                } catch (error) {
                  reject(error);
                }
              });
            }
            
            if (this.state === 'pending') {
              this.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                  try {
                    const x = onFulfilled(this.value);
                    resolve(x);
                  } catch (error) {
                    reject(error);
                  }
                });
              });
              
              this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                  try {
                    const x = onRejected(this.reason);
                    resolve(x);
                  } catch (error) {
                    reject(error);
                  }
                });
              });
            }
          });
        }
        
        catch(onRejected) {
          return this.then(null, onRejected);
        }
      }
      \`\`\`
      
      ## 5. Promise.all
      
      \`\`\`javascript
      Promise.myAll = function(promises) {
        return new Promise((resolve, reject) => {
          if (!Array.isArray(promises)) {
            return reject(new TypeError('arguments must be an array'));
          }
          
          const result = [];
          let count = 0;
          
          if (promises.length === 0) {
            return resolve(result);
          }
          
          promises.forEach((promise, index) => {
            Promise.resolve(promise).then(
              value => {
                result[index] = value;
                count++;
                
                if (count === promises.length) {
                  resolve(result);
                }
              },
              reason => {
                reject(reason);
              }
            );
          });
        });
      };
      
      // 测试
      Promise.myAll([
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3)
      ]).then(console.log); // [1, 2, 3]
      \`\`\`
      
      ## 6. 数组扁平化
      
      \`\`\`javascript
      // 方法1：递归
      function flatten(arr) {
        return arr.reduce((acc, val) => {
          return acc.concat(Array.isArray(val) ? flatten(val) : val);
        }, []);
      }
      
      // 方法2：迭代
      function flatten(arr) {
        const result = [];
        const stack = [...arr];
        
        while (stack.length) {
          const item = stack.pop();
          
          if (Array.isArray(item)) {
            stack.push(...item);
          } else {
            result.unshift(item);
          }
        }
        
        return result;
      }
      
      // 方法3：ES6
      function flatten(arr) {
        return arr.flat(Infinity);
      }
      
      // 测试
      flatten([1, [2, [3, [4, 5]]]]); // [1, 2, 3, 4, 5]
      \`\`\`
      
      ## 7. 数组去重
      
      \`\`\`javascript
      // 方法1：Set
      function unique(arr) {
        return [...new Set(arr)];
      }
      
      // 方法2：filter
      function unique(arr) {
        return arr.filter((item, index) => arr.indexOf(item) === index);
      }
      
      // 方法3：reduce
      function unique(arr) {
        return arr.reduce((acc, item) => {
          return acc.includes(item) ? acc : [...acc, item];
        }, []);
      }
      
      // 对象数组去重
      function uniqueBy(arr, key) {
        const map = new Map();
        return arr.filter(item => {
          if (!map.has(item[key])) {
            map.set(item[key], true);
            return true;
          }
          return false;
        });
      }
      \`\`\`
      
      ## 8. 柯里化
      
      \`\`\`javascript
      /**
       * 柯里化：把接受多个参数的函数变换成接受单一参数的函数
       */
      function curry(fn) {
        return function curried(...args) {
          if (args.length >= fn.length) {
            return fn.apply(this, args);
          } else {
            return function(...args2) {
              return curried.apply(this, args.concat(args2));
            };
          }
        };
      }
      
      // 使用
      function add(a, b, c) {
        return a + b + c;
      }
      
      const curriedAdd = curry(add);
      console.log(curriedAdd(1)(2)(3)); // 6
      console.log(curriedAdd(1, 2)(3)); // 6
      console.log(curriedAdd(1)(2, 3)); // 6
      \`\`\`
      
      ## 9. 发布订阅模式
      
      \`\`\`javascript
      class EventEmitter {
        constructor() {
          this.events = {};
        }
        
        // 订阅
        on(event, callback) {
          if (!this.events[event]) {
            this.events[event] = [];
          }
          this.events[event].push(callback);
        }
        
        // 取消订阅
        off(event, callback) {
          if (!this.events[event]) return;
          
          this.events[event] = this.events[event].filter(cb => cb !== callback);
        }
        
        // 发布
        emit(event, ...args) {
          if (!this.events[event]) return;
          
          this.events[event].forEach(callback => {
            callback(...args);
          });
        }
        
        // 只订阅一次
        once(event, callback) {
          const wrapper = (...args) => {
            callback(...args);
            this.off(event, wrapper);
          };
          this.on(event, wrapper);
        }
      }
      
      // 使用
      const emitter = new EventEmitter();
      
      emitter.on('click', (data) => {
        console.log('clicked:', data);
      });
      
      emitter.emit('click', { x: 100, y: 200 });
      \`\`\`
      
      ## 10. 实现new操作符
      
      \`\`\`javascript
      function myNew(Constructor, ...args) {
        // 1. 创建新对象，原型指向构造函数的prototype
        const obj = Object.create(Constructor.prototype);
        
        // 2. 执行构造函数
        const result = Constructor.apply(obj, args);
        
        // 3. 如果构造函数返回对象，则返回该对象，否则返回新创建的对象
        return result instanceof Object ? result : obj;
      }
      
      // 测试
      function Person(name, age) {
        this.name = name;
        this.age = age;
      }
      
      const person = myNew(Person, 'John', 25);
      console.log(person); // { name: 'John', age: 25 }
      \`\`\`
      
      ## 11. 实现bind
      
      \`\`\`javascript
      Function.prototype.myBind = function(context, ...args) {
        const fn = this;
        
        return function(...args2) {
          return fn.apply(context, args.concat(args2));
        };
      };
      
      // 测试
      const obj = { name: 'John' };
      
      function greet(greeting, punctuation) {
        console.log(\`\${greeting}, \${this.name}\${punctuation}\`);
      }
      
      const boundGreet = greet.myBind(obj, 'Hello');
      boundGreet('!'); // Hello, John!
      \`\`\`
      
      ## 12. 实现call和apply
      
      \`\`\`javascript
      Function.prototype.myCall = function(context, ...args) {
        context = context || window;
        const fn = Symbol();
        context[fn] = this;
        
        const result = context[fn](...args);
        delete context[fn];
        
        return result;
      };
      
      Function.prototype.myApply = function(context, args) {
        context = context || window;
        const fn = Symbol();
        context[fn] = this;
        
        const result = context[fn](...args);
        delete context[fn];
        
        return result;
      };
      
      // 测试
      const obj = { name: 'John' };
      
      function greet(greeting) {
        console.log(\`\${greeting}, \${this.name}\`);
      }
      
      greet.myCall(obj, 'Hello'); // Hello, John
      greet.myApply(obj, ['Hi']); // Hi, John
      \`\`\`
      
      ## 13. 实现instanceof
      
      \`\`\`javascript
      function myInstanceof(obj, Constructor) {
        let proto = Object.getPrototypeOf(obj);
        
        while (proto) {
          if (proto === Constructor.prototype) {
            return true;
          }
          proto = Object.getPrototypeOf(proto);
        }
        
        return false;
      }
      
      // 测试
      console.log(myInstanceof([], Array)); // true
      console.log(myInstanceof({}, Array)); // false
      \`\`\`
      
      ## 14. 数组转树形结构
      
      \`\`\`javascript
      function arrayToTree(arr) {
        const map = {};
        const result = [];
        
        // 先将所有项存入map
        arr.forEach(item => {
          map[item.id] = { ...item, children: [] };
        });
        
        // 建立父子关系
        arr.forEach(item => {
          if (item.parentId) {
            map[item.parentId].children.push(map[item.id]);
          } else {
            result.push(map[item.id]);
          }
        });
        
        return result;
      }
      
      // 测试
      const arr = [
        { id: 1, name: '部门1', parentId: null },
        { id: 2, name: '部门2', parentId: 1 },
        { id: 3, name: '部门3', parentId: 1 },
        { id: 4, name: '部门4', parentId: 2 }
      ];
      
      console.log(arrayToTree(arr));
      \`\`\`
      
      ## 15. LRU缓存
      
      \`\`\`javascript
      class LRUCache {
        constructor(capacity) {
          this.capacity = capacity;
          this.cache = new Map();
        }
        
        get(key) {
          if (!this.cache.has(key)) {
            return -1;
          }
          
          // 更新访问顺序
          const value = this.cache.get(key);
          this.cache.delete(key);
          this.cache.set(key, value);
          
          return value;
        }
        
        put(key, value) {
          // 如果已存在，删除旧的
          if (this.cache.has(key)) {
            this.cache.delete(key);
          }
          
          // 添加新的
          this.cache.set(key, value);
          
          // 超出容量，删除最久未使用的
          if (this.cache.size > this.capacity) {
            const firstKey = this.cache.keys().next().value;
            this.cache.delete(firstKey);
          }
        }
      }
      
      // 测试
      const cache = new LRUCache(2);
      cache.put(1, 1);
      cache.put(2, 2);
      console.log(cache.get(1)); // 1
      cache.put(3, 3); // 淘汰 2
      console.log(cache.get(2)); // -1
      \`\`\`
      
      这15个手写代码一定要熟练掌握，面试基本都会考。
      
      建议每天写一遍，加深理解。
      `
      },
      {
        title: '2025前端面试题汇总：CSS布局与动画实战',
        slug: '2025-frontend-interview-css',
        excerpt: 'CSS面试高频题整理，包括Flex、Grid、定位、BFC、动画等核心知识点。',
        category: '面试准备',
        tags: ['面试', 'CSS', '前端'],
        status: 'published',
        isFeatured: true,
        views: 7890,
        likes: 645,
        publishedAt: new Date('2025-08-25'),
        content: `
      CSS是前端基础，但面试时经常考得很细。整理了常见问题。
      
      ## 一、盒模型
      
      ### Q1: 标准盒模型和IE盒模型的区别？
      
      \`\`\`css
      /* 标准盒模型（content-box） */
      .box {
        box-sizing: content-box; /* 默认值 */
        width: 200px;
        padding: 20px;
        border: 2px solid #000;
      }
      /* 实际宽度 = 200 + 20*2 + 2*2 = 244px */
      
      /* IE盒模型（border-box） */
      .box {
        box-sizing: border-box;
        width: 200px;
        padding: 20px;
        border: 2px solid #000;
      }
      /* 实际宽度 = 200px（包含padding和border） */
      \`\`\`
      
      **推荐使用border-box**：
      \`\`\`css
      *, *::before, *::after {
        box-sizing: border-box;
      }
      \`\`\`
      
      ## 二、布局
      
      ### Q2: Flex布局常用属性？
      
      **容器属性**：
      
      \`\`\`css
      .container {
        display: flex;
        
        /* 主轴方向 */
        flex-direction: row | row-reverse | column | column-reverse;
        
        /* 换行 */
        flex-wrap: nowrap | wrap | wrap-reverse;
        
        /* 主轴对齐 */
        justify-content: flex-start | flex-end | center | space-between | space-around;
        
        /* 交叉轴对齐 */
        align-items: flex-start | flex-end | center | baseline | stretch;
        
        /* 多行对齐 */
        align-content: flex-start | flex-end | center | space-between | space-around;
      }
      \`\`\`
      
      **项目属性**：
      
      \`\`\`css
      .item {
        /* 放大比例 */
        flex-grow: 1;
        
        /* 缩小比例 */
        flex-shrink: 1;
        
        /* 基础大小 */
        flex-basis: auto;
        
        /* 简写 */
        flex: 1; /* flex-grow: 1; flex-shrink: 1; flex-basis: 0%; */
        
        /* 单独对齐 */
        align-self: auto | flex-start | flex-end | center | baseline | stretch;
      }
      \`\`\`
      
      ### Q3: 实现水平垂直居中？
      
      **方法1：Flex布局**
      
      \`\`\`css
      .container {
        display: flex;
        justify-content: center;
        align-items: center;
      }
      \`\`\`
      
      **方法2：Grid布局**
      
      \`\`\`css
      .container {
        display: grid;
        place-items: center;
      }
      \`\`\`
      
      **方法3：绝对定位 + transform**
      
      \`\`\`css
      .container {
        position: relative;
      }
      
      .item {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
      \`\`\`
      
      **方法4：绝对定位 + margin**
      
      \`\`\`css
      .item {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        margin: auto;
        width: 200px;
        height: 200px;
      }
      \`\`\`
      
      ### Q4: Grid布局基础？
      
      \`\`\`css
      .container {
        display: grid;
        
        /* 定义列 */
        grid-template-columns: 200px 1fr 2fr;
        /* 或使用repeat */
        grid-template-columns: repeat(3, 1fr);
        
        /* 定义行 */
        grid-template-rows: 100px auto;
        
        /* 间距 */
        gap: 20px;
        /* 或分开设置 */
        row-gap: 20px;
        column-gap: 10px;
      }
      
      .item {
        /* 占据列 */
        grid-column: 1 / 3; /* 从第1列到第3列 */
        
        /* 占据行 */
        grid-row: 1 / 2;
        
        /* 简写 */
        grid-area: 1 / 1 / 2 / 3; /* row-start / col-start / row-end / col-end */
      }
      \`\`\`
      
      ## 三、定位
      
      ### Q5: position的值有哪些？
      
      \`\`\`css
      /* static - 默认值，正常文档流 */
      position: static;
      
      /* relative - 相对定位，相对自身原位置 */
      .relative {
        position: relative;
        top: 10px; /* 相对原位置向下10px */
      }
      
      /* absolute - 绝对定位，相对最近的已定位祖先元素 */
      .absolute {
        position: absolute;
        top: 0;
        left: 0;
      }
      
      /* fixed - 固定定位，相对视口 */
      .fixed {
        position: fixed;
        bottom: 20px;
        right: 20px;
      }
      
      /* sticky - 粘性定位，relative和fixed的结合 */
      .sticky {
        position: sticky;
        top: 0; /* 滚动到顶部时固定 */
      }
      \`\`\`
      
      ## 四、BFC
      
      ### Q6: 什么是BFC？如何触发？
      
      **BFC（Block Formatting Context）块级格式化上下文**
      
      **触发条件**：
      - float不为none
      - position为absolute或fixed
      - display为inline-block、table-cell、flex、grid等
      - overflow不为visible
      
      **作用**：
      1. 清除浮动
      2. 防止margin重叠
      3. 阻止元素被浮动元素覆盖
      
      \`\`\`css
      /* 清除浮动 */
      .clearfix {
        overflow: hidden; /* 触发BFC */
      }
      
      /* 防止margin重叠 */
      .wrapper {
        overflow: hidden; /* 触发BFC */
      }
      
      .box {
        margin: 20px 0;
      }
      \`\`\`
      
      ## 五、动画
      
      ### Q7: transition和animation的区别？
      
      **transition - 过渡**
      
      \`\`\`css
      .box {
        width: 100px;
        transition: width 0.3s ease;
      }
      
      .box:hover {
        width: 200px; /* 需要触发条件 */
      }
      \`\`\`
      
      **animation - 动画**
      
      \`\`\`css
      @keyframes slideIn {
        0% {
          transform: translateX(-100%);
        }
        100% {
          transform: translateX(0);
        }
      }
      
      .box {
        animation: slideIn 0.5s ease; /* 自动执行 */
      }
      \`\`\`
      
      **区别**：
      - transition需要触发条件（hover、focus等）
      - animation可以自动执行
      - animation可以定义多个关键帧
      - animation可以设置循环次数
      
      ### Q8: 实现一个loading动画？
      
      \`\`\`html
      <div class="loading"></div>
      \`\`\`
      
      \`\`\`css
      .loading {
        width: 50px;
        height: 50px;
        border: 5px solid #f3f3f3;
        border-top: 5px solid #3498db;
        border-radius: 50%;
        animation: spin 1s linear infinite;
      }
      
      @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
      }
      \`\`\`
      
      ## 六、响应式
      
      ### Q9: 移动端适配方案？
      
      **方案1：rem + flexible**
      
      \`\`\`javascript
      // 根据屏幕宽度设置根元素字体大小
      function setRem() {
        const width = document.documentElement.clientWidth;
        document.documentElement.style.fontSize = width / 10 + 'px';
      }
      
      setRem();
      window.addEventListener('resize', setRem);
      \`\`\`
      
      \`\`\`css
      /* 设计稿750px，元素宽度375px */
      .box {
        width: 5rem; /* 375 / 75 = 5 */
      }
      \`\`\`
      
      **方案2：vw**
      
      \`\`\`css
      /* 设计稿750px，元素宽度375px */
      .box {
        width: 50vw; /* 375 / 750 * 100 = 50 */
      }
      \`\`\`
      
      **方案3：媒体查询**
      
      \`\`\`css
      /* 移动端 */
      @media (max-width: 768px) {
        .box {
          width: 100%;
        }
      }
      
      /* 平板 */
      @media (min-width: 769px) and (max-width: 1024px) {
        .box {
          width: 50%;
        }
      }
      
      /* PC端 */
      @media (min-width: 1025px) {
        .box {
          width: 33.33%;
        }
      }
      \`\`\`
      
      ## 七、性能优化
      
      ### Q10: CSS性能优化？
      
      **1. 减少重排和重绘**
      
      \`\`\`css
      /* 使用transform代替top/left */
      .box {
        /* 不好：触发重排 */
        /* left: 100px; */
        
        /* 好：只触发合成 */
        transform: translateX(100px);
      }
      \`\`\`
      
      **2. 使用CSS变量**
      
      \`\`\`css
      :root {
        --primary-color: #3498db;
      }
      
      .button {
        background: var(--primary-color);
      }
      \`\`\`
      
      **3. 避免@import**
      
      \`\`\`html
      <!-- 不好：串行加载 -->
      <style>
        @import url('style.css');
      </style>
      
      <!-- 好：并行加载 -->
      <link rel="stylesheet" href="style.css">
      \`\`\`
      
      **4. 使用will-change提示浏览器**
      
      \`\`\`css
      .box {
        will-change: transform;
      }
      \`\`\`
      
      ## 总结
      
      CSS面试重点：
      - 盒模型和BFC概念要清楚
      - Flex和Grid布局要熟练
      - 定位方式要理解透彻
      - 动画性能优化要了解
      - 移动端适配方案要掌握
      
      实际项目中多实践，遇到问题多思考为什么，面试时才能答得深入。
      `
      },
      {
        title: '2025前端面试题汇总：浏览器原理与HTTP协议详解',
        slug: '2025-frontend-interview-browser-network',
        excerpt: '浏览器工作原理、HTTP协议、性能优化等网络相关面试高频题整理。',
        category: '面试准备',
        tags: ['面试', '浏览器', 'HTTP'],
        status: 'published',
        isFeatured: true,
        views: 8760,
        likes: 712,
        publishedAt: new Date('2025-08-23'),
        content: `
      浏览器和网络是前端面试的必考内容，整理了常见问题。
      
      ## 一、浏览器工作原理
      
      ### Q1: 从输入URL到页面展示，发生了什么？
      
      **完整流程**：
      
      1. **DNS解析**
      \`\`\`
      www.example.com -> 192.168.1.1
      - 浏览器缓存
      - 系统缓存
      - 路由器缓存
      - ISP DNS服务器
      - 根域名服务器
      \`\`\`
      
      2. **建立TCP连接**
      \`\`\`
      三次握手：
      客户端 -> SYN -> 服务器
      客户端 <- SYN+ACK <- 服务器
      客户端 -> ACK -> 服务器
      \`\`\`
      
      3. **发送HTTP请求**
      \`\`\`http
      GET /index.html HTTP/1.1
      Host: www.example.com
      User-Agent: Chrome/120.0
      Accept: text/html
      \`\`\`
      
      4. **服务器处理并返回响应**
      
      5. **浏览器解析渲染**
      - 解析HTML构建DOM树
      - 解析CSS构建CSSOM树
      - 合并生成渲染树
      - 布局（Layout）
      - 绘制（Paint）
      - 合成（Composite）
      
      6. **关闭连接**
      \`\`\`
      四次挥手
      \`\`\`
      
      ### Q2: 浏览器渲染流程？
      
      \`\`\`
      HTML -> DOM树
      CSS -> CSSOM树
      DOM + CSSOM -> 渲染树 -> 布局 -> 绘制 -> 合成 -> 显示
      \`\`\`
      
      **详细步骤**：
      
      1. **解析HTML**
      \`\`\`html
      <div class="box">
        <p>Hello</p>
      </div>
      \`\`\`
      生成DOM树
      
      2. **解析CSS**
      \`\`\`css
      .box { width: 100px; }
      p { color: red; }
      \`\`\`
      生成CSSOM树
      
      3. **构建渲染树**
      - 遍历DOM树的每个可见节点
      - 找到对应的CSSOM规则并应用
      - 发射可见节点及其内容和样式
      
      4. **布局（Layout/Reflow）**
      - 计算每个节点的位置和大小
      
      5. **绘制（Paint）**
      - 将节点转换为屏幕上的实际像素
      
      6. **合成（Composite）**
      - 将多个图层合成最终页面
      
      ### Q3: 重排和重绘的区别？
      
      **重排（Reflow）**：
      - 元素的位置、大小发生变化
      - 需要重新计算布局
      - 代价较大
      
      触发条件：
      \`\`\`javascript
      // 会触发重排
      element.style.width = '200px';
      element.style.height = '100px';
      element.style.margin = '10px';
      element.offsetWidth; // 读取布局信息也会触发
      \`\`\`
      
      **重绘（Repaint）**：
      - 元素外观改变，但布局不变
      - 只需要重新绘制
      - 代价较小
      
      触发条件：
      \`\`\`javascript
      // 只触发重绘
      element.style.color = 'red';
      element.style.backgroundColor = 'blue';
      \`\`\`
      
      **优化方法**：
      \`\`\`javascript
      // 不好：多次操作DOM
      element.style.width = '200px';
      element.style.height = '100px';
      element.style.margin = '10px';
      
      // 好：批量修改
      element.style.cssText = 'width: 200px; height: 100px; margin: 10px;';
      
      // 或使用class
      element.className = 'new-style';
      
      // 使用transform代替top/left
      element.style.transform = 'translateX(100px)'; // 不触发重排
      \`\`\`
      
      ## 二、HTTP协议
      
      ### Q4: HTTP状态码？
      
      **1xx - 信息性**
      - 100 Continue：继续请求
      
      **2xx - 成功**
      - 200 OK：成功
      - 201 Created：已创建
      - 204 No Content：无内容
      
      **3xx - 重定向**
      - 301 Moved Permanently：永久重定向
      - 302 Found：临时重定向
      - 304 Not Modified：未修改（使用缓存）
      
      **4xx - 客户端错误**
      - 400 Bad Request：请求错误
      - 401 Unauthorized：未授权
      - 403 Forbidden：禁止访问
      - 404 Not Found：未找到
      
      **5xx - 服务器错误**
      - 500 Internal Server Error：服务器错误
      - 502 Bad Gateway：网关错误
      - 503 Service Unavailable：服务不可用
      
      ### Q5: HTTP缓存机制？
      
      **强缓存**：
      \`\`\`http
      # 响应头
      Cache-Control: max-age=3600  # 1小时内使用缓存
      Expires: Wed, 21 Oct 2025 07:28:00 GMT  # 过期时间
      \`\`\`
      
      **协商缓存**：
      \`\`\`http
      # 首次请求
      响应头：
      Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT
      ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
      
      # 再次请求
      请求头：
      If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT
      If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
      
      # 如果未修改，返回304
      \`\`\`
      
      **缓存策略**：
      \`\`\`javascript
      // HTML - 协商缓存
      Cache-Control: no-cache
      
      // CSS/JS - 强缓存
      Cache-Control: max-age=31536000  // 1年
      // 文件名带hash，更新时改变文件名
      
      // 图片 - 强缓存
      Cache-Control: max-age=86400  // 1天
      \`\`\`
      
      ### Q6: HTTP/1.1、HTTP/2、HTTP/3的区别？
      
      **HTTP/1.1**：
      - 持久连接（Keep-Alive）
      - 管道化（但有队头阻塞问题）
      - 分块传输
      
      **HTTP/2**：
      - 二进制分帧
      - 多路复用（一个连接并发多个请求）
      - 服务器推送
      - 头部压缩（HPACK）
      
      \`\`\`javascript
      // HTTP/1.1：6个请求需要多个连接
      请求1 ---|
      请求2 ---|
      请求3 ---|
      请求4 ---|
      请求5 ---|
      请求6 ---|
      
      // HTTP/2：一个连接并发
      请求1 -|
      请求2 -|
      请求3 -|-> 同一个连接
      请求4 -|
      请求5 -|
      请求6 -|
      \`\`\`
      
      **HTTP/3**：
      - 基于QUIC（UDP）
      - 解决TCP队头阻塞
      - 0-RTT连接建立
      - 更好的移动网络支持
      
      ### Q7: HTTPS原理？
      
      **加密过程**：
      
      1. **客户端请求**
      \`\`\`
      客户端 -> Hello（支持的加密算法） -> 服务器
      \`\`\`
      
      2. **服务器响应**
      \`\`\`
      服务器 -> 证书 + 公钥 -> 客户端
      \`\`\`
      
      3. **客户端验证证书**
      - 检查证书是否有效
      - 检查域名是否匹配
      - 检查证书是否过期
      
      4. **生成对称密钥**
      \`\`\`
      客户端 -> 用公钥加密的对称密钥 -> 服务器
      服务器用私钥解密得到对称密钥
      \`\`\`
      
      5. **后续通信使用对称加密**
      
      **为什么不全用非对称加密**？
      - 非对称加密计算量大，性能差
      - 对称加密快，适合大量数据传输
      
      ## 三、跨域
      
      ### Q8: 跨域的原因和解决方案？
      
      **同源策略**：协议、域名、端口都相同
      
      \`\`\`
      http://www.example.com:80/page1.html
      
      协议：http://
      域名：www.example.com
      端口：80
      \`\`\`
      
      **解决方案1：CORS**
      
      \`\`\`javascript
      // 服务器设置响应头
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
      res.setHeader('Access-Control-Allow-Credentials', 'true');
      \`\`\`
      
      **解决方案2：代理**
      
      \`\`\`javascript
      // webpack dev server
      devServer: {
        proxy: {
          '/api': {
            target: 'http://api.example.com',
            changeOrigin: true,
            pathRewrite: { '^/api': '' }
          }
        }
      }
      \`\`\`
      
      **解决方案3：JSONP**（只支持GET）
      
      \`\`\`javascript
      function jsonp(url, callback) {
        const script = document.createElement('script');
        script.src = \`\${url}?callback=\${callback}\`;
        document.body.appendChild(script);
      }
      
      function handleResponse(data) {
        console.log(data);
      }
      
      jsonp('http://api.example.com/data', 'handleResponse');
      \`\`\`
      
      ## 四、性能优化
      
      ### Q9: 前端性能优化手段？
      
      **1. 资源优化**
      \`\`\`javascript
      // 代码分割
      import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {
        // ...
      });
      
      // 图片懒加载
      const img = new Image();
      img.loading = 'lazy';
      img.src = 'image.jpg';
      
      // 使用WebP格式
      <picture>
        <source srcset="image.webp" type="image/webp">
        <img src="image.jpg" alt="">
      </picture>
      \`\`\`
      
      **2. 网络优化**
      \`\`\`html
      <!-- DNS预解析 -->
      <link rel="dns-prefetch" href="//api.example.com">
      
      <!-- 预连接 -->
      <link rel="preconnect" href="//api.example.com">
      
      <!-- 预加载 -->
      <link rel="preload" href="style.css" as="style">
      
      <!-- 预获取 -->
      <link rel="prefetch" href="next-page.js">
      \`\`\`
      
      **3. 渲染优化**
      \`\`\`javascript
      // 使用requestAnimationFrame
      function animate() {
        // 动画逻辑
        requestAnimationFrame(animate);
      }
      requestAnimationFrame(animate);
      
      // 虚拟滚动
      // 只渲染可见区域的列表项
      
      // 防抖和节流
      function debounce(fn, delay) {
        let timer = null;
        return function(...args) {
          clearTimeout(timer);
          timer = setTimeout(() => fn.apply(this, args), delay);
        };
      }
      
      function throttle(fn, delay) {
        let last = 0;
        return function(...args) {
          const now = Date.now();
          if (now - last > delay) {
            fn.apply(this, args);
            last = now;
          }
        };
      }
      \`\`\`
      
      ### Q10: Web Vitals指标？
      
      **核心指标**：
      
      1. **LCP（Largest Contentful Paint）**
      - 最大内容绘制时间
      - 目标：< 2.5s
      
      2. **FID（First Input Delay）**
      - 首次输入延迟
      - 目标：< 100ms
      
      3. **CLS（Cumulative Layout Shift）**
      - 累积布局偏移
      - 目标：< 0.1
      
      **优化方法**：
      
      \`\`\`javascript
      // 使用web-vitals库监控
      import { getCLS, getFID, getLCP } from 'web-vitals';
      
      getCLS(console.log);
      getFID(console.log);
      getLCP(console.log);
      
      // 优化LCP
      // - 优化服务器响应时间
      // - 使用CDN
      // - 优化图片
      // - 移除渲染阻塞资源
      
      // 优化FID
      // - 减少JavaScript执行时间
      // - 代码分割
      // - 移除未使用的代码
      
      // 优化CLS
      // - 为图片设置固定尺寸
      // - 不在现有内容上方插入内容
      // - 使用transform动画代替改变布局的属性
      \`\`\`
      
      ## 五、安全
      
      ### Q11: 常见的Web安全问题？
      
      **1. XSS（跨站脚本攻击）**
      
      \`\`\`javascript
      // 不安全：直接插入用户输入
      element.innerHTML = userInput;
      
      // 安全：转义特殊字符
      function escapeHtml(str) {
        return str
          .replace(/&/g, '&amp;')
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;')
          .replace(/"/g, '&quot;')
          .replace(/'/g, '&#39;');
      }
      
      element.textContent = userInput; // 或使用textContent
      \`\`\`
      
      **2. CSRF（跨站请求伪造）**
      
      \`\`\`javascript
      // 防御方法1：CSRF Token
      <input type="hidden" name="csrf_token" value="random_token">
      
      // 防御方法2：SameSite Cookie
      Set-Cookie: session=abc123; SameSite=Strict
      
      // 防御方法3：验证Referer
      if (req.headers.referer !== 'https://trusted-site.com') {
        return res.status(403).send('Forbidden');
      }
      \`\`\`
      
      **3. SQL注入**（前端要注意传参）
      
      \`\`\`javascript
      // 不安全
      const query = \`SELECT * FROM users WHERE id = \${userId}\`;
      
      // 安全：使用参数化查询
      const query = 'SELECT * FROM users WHERE id = ?';
      db.query(query, [userId]);
      \`\`\`
      
      ## 总结
      
      浏览器和网络面试重点：
      - 浏览器渲染流程要清楚
      - HTTP协议和缓存机制要熟悉
      - 性能优化要有实践经验
      - 安全问题要有防范意识
      
      建议多读Chrome开发者文档，理解底层原理。
      `
      },
      {
        title: '2025前端面试题汇总：常考算法题精选50道',
        slug: '2025-frontend-interview-algorithms',
        excerpt: '前端面试常考算法题整理，包括数组、字符串、链表、树、动态规划等经典题目。',
        category: '面试准备',
        tags: ['面试', '算法', 'LeetCode'],
        status: 'published',
        isFeatured: true,
        views: 11230,
        likes: 967,
        publishedAt: new Date('2025-08-21'),
        content: `
      前端面试也会考算法，整理了常见题目和解法。
      
      ## 一、数组
      
      ### Q1: 两数之和（LeetCode 1）
      
      给定数组和目标值，找出和为目标值的两个数的索引。
      
      \`\`\`javascript
      // 方法1：暴力法 O(n²)
      function twoSum(nums, target) {
        for (let i = 0; i < nums.length; i++) {
          for (let j = i + 1; j < nums.length; j++) {
            if (nums[i] + nums[j] === target) {
              return [i, j];
            }
          }
        }
        return [];
      }
      
      // 方法2：哈希表 O(n)
      function twoSum(nums, target) {
        const map = new Map();
        for (let i = 0; i < nums.length; i++) {
          const complement = target - nums[i];
          if (map.has(complement)) {
            return [map.get(complement), i];
          }
          map.set(nums[i], i);
        }
        return [];
      }
      
      // 测试
      console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]
      \`\`\`
      
      ### Q2: 数组去重
      
      \`\`\`javascript
      const arr = [1, 2, 2, 3, 4, 4, 5];
      
      // 方法1：Set
      const unique1 = [...new Set(arr)];
      
      // 方法2：filter
      const unique2 = arr.filter((item, index) => arr.indexOf(item) === index);
      
      // 方法3：reduce
      const unique3 = arr.reduce((acc, cur) => {
        return acc.includes(cur) ? acc : [...acc, cur];
      }, []);
      
      // 方法4：Map
      const unique4 = [...new Map(arr.map(item => [item, item])).values()];
      
      console.log(unique1); // [1, 2, 3, 4, 5]
      \`\`\`
      
      ### Q3: 数组扁平化
      
      \`\`\`javascript
      const arr = [1, [2, [3, [4, 5]]]];
      
      // 方法1：flat
      const flat1 = arr.flat(Infinity);
      
      // 方法2：递归
      function flatten(arr) {
        return arr.reduce((acc, cur) => {
          return acc.concat(Array.isArray(cur) ? flatten(cur) : cur);
        }, []);
      }
      
      // 方法3：栈
      function flatten(arr) {
        const stack = [...arr];
        const result = [];
        
        while (stack.length) {
          const item = stack.pop();
          if (Array.isArray(item)) {
            stack.push(...item);
          } else {
            result.unshift(item);
          }
        }
        
        return result;
      }
      
      console.log(flatten(arr)); // [1, 2, 3, 4, 5]
      \`\`\`
      
      ### Q4: 最大子数组和（LeetCode 53）
      
      \`\`\`javascript
      // 动态规划
      function maxSubArray(nums) {
        let max = nums[0];
        let sum = nums[0];
        
        for (let i = 1; i < nums.length; i++) {
          sum = Math.max(nums[i], sum + nums[i]);
          max = Math.max(max, sum);
        }
        
        return max;
      }
      
      console.log(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])); // 6
      // 最大子数组 [4, -1, 2, 1]
      \`\`\`
      
      ## 二、字符串
      
      ### Q5: 反转字符串
      
      \`\`\`javascript
      const str = 'hello';
      
      // 方法1
      const reversed1 = str.split('').reverse().join('');
      
      // 方法2：for循环
      function reverse(str) {
        let result = '';
        for (let i = str.length - 1; i >= 0; i--) {
          result += str[i];
        }
        return result;
      }
      
      // 方法3：reduce
      const reversed3 = str.split('').reduce((acc, char) => char + acc, '');
      
      console.log(reversed1); // 'olleh'
      \`\`\`
      
      ### Q6: 回文字符串判断
      
      \`\`\`javascript
      function isPalindrome(str) {
        // 去除非字母数字字符，转小写
        str = str.toLowerCase().replace(/[^a-z0-9]/g, '');
        
        let left = 0;
        let right = str.length - 1;
        
        while (left < right) {
          if (str[left] !== str[right]) {
            return false;
          }
          left++;
          right--;
        }
        
        return true;
      }
      
      console.log(isPalindrome('A man, a plan, a canal: Panama')); // true
      \`\`\`
      
      ### Q7: 最长公共前缀（LeetCode 14）
      
      \`\`\`javascript
      function longestCommonPrefix(strs) {
        if (strs.length === 0) return '';
        
        let prefix = strs[0];
        
        for (let i = 1; i < strs.length; i++) {
          while (strs[i].indexOf(prefix) !== 0) {
            prefix = prefix.slice(0, -1);
            if (prefix === '') return '';
          }
        }
        
        return prefix;
      }
      
      console.log(longestCommonPrefix(['flower', 'flow', 'flight'])); // 'fl'
      \`\`\`
      
      ### Q8: 字符串中的第一个唯一字符（LeetCode 387）
      
      \`\`\`javascript
      function firstUniqChar(s) {
        const map = new Map();
        
        // 统计每个字符出现次数
        for (const char of s) {
          map.set(char, (map.get(char) || 0) + 1);
        }
        
        // 找到第一个出现一次的字符
        for (let i = 0; i < s.length; i++) {
          if (map.get(s[i]) === 1) {
            return i;
          }
        }
        
        return -1;
      }
      
      console.log(firstUniqChar('leetcode')); // 0
      console.log(firstUniqChar('loveleetcode')); // 2
      \`\`\`
      
      ## 三、链表
      
      ### Q9: 反转链表（LeetCode 206）
      
      \`\`\`javascript
      function ListNode(val, next = null) {
        this.val = val;
        this.next = next;
      }
      
      // 方法1：迭代
      function reverseList(head) {
        let prev = null;
        let curr = head;
        
        while (curr) {
          const next = curr.next;
          curr.next = prev;
          prev = curr;
          curr = next;
        }
        
        return prev;
      }
      
      // 方法2：递归
      function reverseList(head) {
        if (!head || !head.next) return head;
        
        const newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        
        return newHead;
      }
      \`\`\`
      
      ### Q10: 合并两个有序链表（LeetCode 21）
      
      \`\`\`javascript
      function mergeTwoLists(l1, l2) {
        const dummy = new ListNode(0);
        let curr = dummy;
        
        while (l1 && l2) {
          if (l1.val < l2.val) {
            curr.next = l1;
            l1 = l1.next;
          } else {
            curr.next = l2;
            l2 = l2.next;
          }
          curr = curr.next;
        }
        
        curr.next = l1 || l2;
        
        return dummy.next;
      }
      \`\`\`
      
      ## 四、树
      
      ### Q11: 二叉树的最大深度（LeetCode 104）
      
      \`\`\`javascript
      function maxDepth(root) {
        if (!root) return 0;
        
        const left = maxDepth(root.left);
        const right = maxDepth(root.right);
        
        return Math.max(left, right) + 1;
      }
      \`\`\`
      
      ### Q12: 对称二叉树（LeetCode 101）
      
      \`\`\`javascript
      function isSymmetric(root) {
        if (!root) return true;
        
        function isMirror(left, right) {
          if (!left && !right) return true;
          if (!left || !right) return false;
          
          return left.val === right.val &&
                 isMirror(left.left, right.right) &&
                 isMirror(left.right, right.left);
        }
        
        return isMirror(root.left, root.right);
      }
      \`\`\`
      
      ### Q13: 二叉树的层序遍历（LeetCode 102）
      
      \`\`\`javascript
      function levelOrder(root) {
        if (!root) return [];
        
        const result = [];
        const queue = [root];
        
        while (queue.length) {
          const levelSize = queue.length;
          const currentLevel = [];
          
          for (let i = 0; i < levelSize; i++) {
            const node = queue.shift();
            currentLevel.push(node.val);
            
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
          }
          
          result.push(currentLevel);
        }
        
        return result;
      }
      \`\`\`
      
      ## 五、动态规划
      
      ### Q14: 爬楼梯（LeetCode 70）
      
      \`\`\`javascript
      // 每次可以爬1或2个台阶，求爬到n阶有多少种方法
      
      // 方法1：递归（会超时）
      function climbStairs(n) {
        if (n <= 2) return n;
        return climbStairs(n - 1) + climbStairs(n - 2);
      }
      
      // 方法2：动态规划
      function climbStairs(n) {
        if (n <= 2) return n;
        
        const dp = [0, 1, 2];
        
        for (let i = 3; i <= n; i++) {
          dp[i] = dp[i - 1] + dp[i - 2];
        }
        
        return dp[n];
      }
      
      // 方法3：空间优化
      function climbStairs(n) {
        if (n <= 2) return n;
        
        let prev = 1;
        let curr = 2;
        
        for (let i = 3; i <= n; i++) {
          const temp = curr;
          curr = prev + curr;
          prev = temp;
        }
        
        return curr;
      }
      
      console.log(climbStairs(5)); // 8
      \`\`\`
      
      ### Q15: 打家劫舍（LeetCode 198）
      
      \`\`\`javascript
      // 不能偷相邻的房子，求最大金额
      function rob(nums) {
        if (nums.length === 0) return 0;
        if (nums.length === 1) return nums[0];
        
        let prev = 0;
        let curr = 0;
        
        for (const num of nums) {
          const temp = curr;
          curr = Math.max(curr, prev + num);
          prev = temp;
        }
        
        return curr;
      }
      
      console.log(rob([2, 7, 9, 3, 1])); // 12
      // 偷第1、3、5个房子：2 + 9 + 1 = 12
      \`\`\`
      
      ## 六、排序和搜索
      
      ### Q16: 快速排序
      
      \`\`\`javascript
      function quickSort(arr) {
        if (arr.length <= 1) return arr;
        
        const pivot = arr[Math.floor(arr.length / 2)];
        const left = arr.filter(x => x < pivot);
        const middle = arr.filter(x => x === pivot);
        const right = arr.filter(x => x > pivot);
        
        return [...quickSort(left), ...middle, ...quickSort(right)];
      }
      
      console.log(quickSort([3, 1, 4, 1, 5, 9, 2, 6]));
      \`\`\`
      
      ### Q17: 二分查找
      
      \`\`\`javascript
      function binarySearch(arr, target) {
        let left = 0;
        let right = arr.length - 1;
        
        while (left <= right) {
          const mid = Math.floor((left + right) / 2);
          
          if (arr[mid] === target) {
            return mid;
          } else if (arr[mid] < target) {
            left = mid + 1;
          } else {
            right = mid - 1;
          }
        }
        
        return -1;
      }
      
      console.log(binarySearch([1, 2, 3, 4, 5], 3)); // 2
      \`\`\`
      
      ## 七、常用技巧
      
      ### Q18: 防抖和节流
      
      \`\`\`javascript
      // 防抖：延迟执行，频繁触发会重新计时
      function debounce(fn, delay) {
        let timer = null;
        return function(...args) {
          clearTimeout(timer);
          timer = setTimeout(() => {
            fn.apply(this, args);
          }, delay);
        };
      }
      
      // 节流：固定时间内只执行一次
      function throttle(fn, delay) {
        let last = 0;
        return function(...args) {
          const now = Date.now();
          if (now - last > delay) {
            fn.apply(this, args);
            last = now;
          }
        };
      }
      
      // 使用
      const handleInput = debounce((e) => {
        console.log('搜索:', e.target.value);
      }, 500);
      
      const handleScroll = throttle(() => {
        console.log('滚动位置:', window.scrollY);
      }, 200);
      \`\`\`
      
      ### Q19: 深拷贝
      
      \`\`\`javascript
      function deepClone(obj, map = new WeakMap()) {
        // 处理null和非对象
        if (obj === null || typeof obj !== 'object') {
          return obj;
        }
        
        // 处理日期
        if (obj instanceof Date) {
          return new Date(obj);
        }
        
        // 处理正则
        if (obj instanceof RegExp) {
          return new RegExp(obj);
        }
        
        // 处理循环引用
        if (map.has(obj)) {
          return map.get(obj);
        }
        
        // 处理数组和对象
        const clone = Array.isArray(obj) ? [] : {};
        map.set(obj, clone);
        
        for (const key in obj) {
          if (obj.hasOwnProperty(key)) {
            clone[key] = deepClone(obj[key], map);
          }
        }
        
        return clone;
      }
      
      // 测试
      const obj = { a: 1, b: { c: 2 } };
      obj.self = obj; // 循环引用
      
      const cloned = deepClone(obj);
      console.log(cloned);
      \`\`\`
      
      ### Q20: Promise实现
      
      \`\`\`javascript
      class MyPromise {
        constructor(executor) {
          this.state = 'pending';
          this.value = undefined;
          this.reason = undefined;
          this.onFulfilledCallbacks = [];
          this.onRejectedCallbacks = [];
          
          const resolve = (value) => {
            if (this.state === 'pending') {
              this.state = 'fulfilled';
              this.value = value;
              this.onFulfilledCallbacks.forEach(fn => fn());
            }
          };
          
          const reject = (reason) => {
            if (this.state === 'pending') {
              this.state = 'rejected';
              this.reason = reason;
              this.onRejectedCallbacks.forEach(fn => fn());
            }
          };
          
          try {
            executor(resolve, reject);
          } catch (error) {
            reject(error);
          }
        }
        
        then(onFulfilled, onRejected) {
          onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
          onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e };
          
          const promise2 = new MyPromise((resolve, reject) => {
            if (this.state === 'fulfilled') {
              setTimeout(() => {
                try {
                  const x = onFulfilled(this.value);
                  resolve(x);
                } catch (error) {
                  reject(error);
                }
              });
            }
            
            if (this.state === 'rejected') {
              setTimeout(() => {
                try {
                  const x = onRejected(this.reason);
                  resolve(x);
                } catch (error) {
                  reject(error);
                }
              });
            }
            
            if (this.state === 'pending') {
              this.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                  try {
                    const x = onFulfilled(this.value);
                    resolve(x);
                  } catch (error) {
                    reject(error);
                  }
                });
              });
              
              this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                  try {
                    const x = onRejected(this.reason);
                    resolve(x);
                  } catch (error) {
                    reject(error);
                  }
                });
              });
            }
          });
          
          return promise2;
        }
      }
      \`\`\`
      
      ## 总结
      
      算法面试准备建议：
      - 每天至少做1道LeetCode题
      - 重点掌握数组、字符串、链表、树
      - 理解常见算法思想：双指针、滑动窗口、动态规划
      - 多总结，整理模板
      - 刷题顺序：简单 -> 中等 -> 困难
      
      常见题目分类：
      - 数组：双指针、滑动窗口、前缀和
      - 字符串：双指针、哈希表、KMP
      - 链表：快慢指针、虚拟头节点
      - 树：递归、层序遍历、DFS/BFS
      - 动态规划：爬楼梯、打家劫舍、背包问题
      
      记住：算法题不是考数学，是考编程思维和代码能力。
      `
      },
      {
        title: '2025前端面试题汇总：Webpack与Vite构建工具详解',
        slug: '2025-frontend-interview-build-tools',
        excerpt: '前端工程化面试高频题，包括Webpack配置、优化、Vite原理等核心知识点。',
        category: '面试准备',
        tags: ['面试', 'Webpack', 'Vite'],
        status: 'published',
        isFeatured: true,
        views: 6780,
        likes: 523,
        publishedAt: new Date('2025-08-19'),
        content: `
      前端工程化是现代开发必备技能，整理了构建工具相关的面试题。
      
      ## 一、Webpack基础
      
      ### Q1: Webpack的核心概念？
      
      **1. Entry（入口）**
      \`\`\`javascript
      module.exports = {
        entry: './src/index.js',
        // 或多入口
        entry: {
          app: './src/app.js',
          admin: './src/admin.js'
        }
      };
      \`\`\`
      
      **2. Output（输出）**
      \`\`\`javascript
      module.exports = {
        output: {
          path: path.resolve(__dirname, 'dist'),
          filename: '[name].[contenthash].js',
          clean: true // 清理旧文件
        }
      };
      \`\`\`
      
      **3. Loader（加载器）**
      \`\`\`javascript
      module.exports = {
        module: {
          rules: [
            {
              test: /\.css$/,
              use: ['style-loader', 'css-loader']
            },
            {
              test: /\.js$/,
              exclude: /node_modules/,
              use: {
                loader: 'babel-loader',
                options: {
                  presets: ['@babel/preset-env']
                }
              }
            },
            {
              test: /\.(png|jpg|gif)$/,
              type: 'asset/resource'
            }
          ]
        }
      };
      \`\`\`
      
      **4. Plugin（插件）**
      \`\`\`javascript
      const HtmlWebpackPlugin = require('html-webpack-plugin');
      const MiniCssExtractPlugin = require('mini-css-extract-plugin');
      
      module.exports = {
        plugins: [
          new HtmlWebpackPlugin({
            template: './src/index.html'
          }),
          new MiniCssExtractPlugin({
            filename: '[name].[contenthash].css'
          })
        ]
      };
      \`\`\`
      
      ### Q2: Loader和Plugin的区别？
      
      **Loader**：
      - 用于转换模块的源代码
      - 在模块加载时执行
      - 本质是一个函数，接收源码，返回转换后的代码
      
      \`\`\`javascript
      // 自定义loader示例
      module.exports = function(source) {
        // source是文件内容
        return source.replace(/console\.log/g, '');
      };
      \`\`\`
      
      **Plugin**：
      - 用于扩展Webpack功能
      - 在整个编译生命周期执行
      - 本质是一个类，包含apply方法
      
      \`\`\`javascript
      // 自定义plugin示例
      class MyPlugin {
        apply(compiler) {
          compiler.hooks.emit.tap('MyPlugin', (compilation) => {
            console.log('Webpack构建过程');
          });
        }
      }
      
      module.exports = MyPlugin;
      \`\`\`
      
      ### Q3: Webpack的构建流程？
      
      **完整流程**：
      
      1. **初始化**
      - 读取配置文件
      - 合并配置参数
      - 初始化编译器对象
      
      2. **编译**
      - 从entry开始，递归找出所有依赖
      - 调用loader转换模块
      - 解析模块依赖关系
      
      3. **输出**
      - 根据依赖关系组装成chunk
      - 将chunk转换为文件
      - 输出到文件系统
      
      \`\`\`javascript
      // 简化流程
      入口文件 -> 解析依赖 -> Loader转换 -> 生成AST -> 
      收集依赖 -> 递归处理 -> 生成Chunk -> 输出文件
      \`\`\`
      
      ## 二、Webpack优化
      
      ### Q4: 如何优化Webpack构建速度？
      
      **1. 缩小文件搜索范围**
      
      \`\`\`javascript
      module.exports = {
        resolve: {
          // 指定查找模块的目录
          modules: [path.resolve(__dirname, 'src'), 'node_modules'],
          // 自动解析的扩展名
          extensions: ['.js', '.jsx', '.json'],
          // 别名
          alias: {
            '@': path.resolve(__dirname, 'src')
          }
        },
        module: {
          rules: [
            {
              test: /\.js$/,
              // 明确范围
              include: path.resolve(__dirname, 'src'),
              exclude: /node_modules/,
              use: 'babel-loader'
            }
          ]
        }
      };
      \`\`\`
      
      **2. 使用缓存**
      
      \`\`\`javascript
      module.exports = {
        cache: {
          type: 'filesystem', // 使用文件系统缓存
          cacheDirectory: path.resolve(__dirname, '.webpack_cache')
        },
        module: {
          rules: [
            {
              test: /\.js$/,
              use: {
                loader: 'babel-loader',
                options: {
                  cacheDirectory: true // Babel缓存
                }
              }
            }
          ]
        }
      };
      \`\`\`
      
      **3. 多进程构建**
      
      \`\`\`javascript
      const TerserPlugin = require('terser-webpack-plugin');
      
      module.exports = {
        module: {
          rules: [
            {
              test: /\.js$/,
              use: [
                {
                  loader: 'thread-loader', // 多进程loader
                  options: {
                    workers: 4
                  }
                },
                'babel-loader'
              ]
            }
          ]
        },
        optimization: {
          minimizer: [
            new TerserPlugin({
              parallel: true // 多进程压缩
            })
          ]
        }
      };
      \`\`\`
      
      **4. DLL动态链接库**
      
      \`\`\`javascript
      // webpack.dll.config.js
      module.exports = {
        entry: {
          vendor: ['react', 'react-dom', 'lodash']
        },
        output: {
          filename: '[name].dll.js',
          path: path.resolve(__dirname, 'dll'),
          library: '[name]_[hash]'
        },
        plugins: [
          new webpack.DllPlugin({
            name: '[name]_[hash]',
            path: path.resolve(__dirname, 'dll/[name].manifest.json')
          })
        ]
      };
      
      // webpack.config.js
      module.exports = {
        plugins: [
          new webpack.DllReferencePlugin({
            manifest: require('./dll/vendor.manifest.json')
          })
        ]
      };
      \`\`\`
      
      ### Q5: 如何优化打包体积？
      
      **1. 代码分割**
      
      \`\`\`javascript
      module.exports = {
        optimization: {
          splitChunks: {
            chunks: 'all',
            cacheGroups: {
              vendor: {
                test: /[\\/]node_modules[\\/]/,
                name: 'vendors',
                priority: 10
              },
              common: {
                minChunks: 2,
                name: 'common',
                priority: 5
              }
            }
          }
        }
      };
      \`\`\`
      
      **2. Tree Shaking**
      
      \`\`\`javascript
      // package.json
      {
        "sideEffects": [
          "*.css",
          "*.scss"
        ]
      }
      
      // webpack.config.js
      module.exports = {
        mode: 'production', // production模式自动开启
        optimization: {
          usedExports: true
        }
      };
      \`\`\`
      
      **3. 压缩代码**
      
      \`\`\`javascript
      const TerserPlugin = require('terser-webpack-plugin');
      const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
      
      module.exports = {
        optimization: {
          minimize: true,
          minimizer: [
            new TerserPlugin({
              terserOptions: {
                compress: {
                  drop_console: true // 删除console
                }
              }
            }),
            new CssMinimizerPlugin()
          ]
        }
      };
      \`\`\`
      
      **4. 按需加载**
      
      \`\`\`javascript
      // 动态导入
      button.addEventListener('click', () => {
        import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {
          console.log(_.join(['Hello', 'webpack'], ' '));
        });
      });
      
      // React路由懒加载
      const Home = lazy(() => import('./pages/Home'));
      const About = lazy(() => import('./pages/About'));
      \`\`\`
      
      ## 三、Vite
      
      ### Q6: Vite的原理和优势？
      
      **核心原理**：
      - 开发环境使用ES Module（不打包）
      - 利用浏览器原生支持ESM
      - 按需编译，即时响应
      
      **优势**：
      
      1. **极快的冷启动**
      \`\`\`javascript
      // Webpack: 需要打包所有模块
      启动时间 = 分析依赖 + 打包所有文件
      
      // Vite: 直接启动开发服务器
      启动时间 ≈ 几毫秒
      \`\`\`
      
      2. **即时的模块热更新**
      \`\`\`javascript
      // Webpack HMR: 需要重新打包模块及其依赖
      更新时间 = 重新打包相关模块
      
      // Vite HMR: 只更新修改的模块
      更新时间 ≈ 毫秒级
      \`\`\`
      
      3. **按需编译**
      \`\`\`javascript
      // 浏览器请求什么，就编译什么
      import { debounce } from 'lodash-es';
      // 只编译debounce，不编译整个lodash
      \`\`\`
      
      ### Q7: Vite配置示例？
      
      \`\`\`javascript
      // vite.config.js
      import { defineConfig } from 'vite';
      import vue from '@vitejs/plugin-vue';
      import path from 'path';
      
      export default defineConfig({
        plugins: [vue()],
        
        resolve: {
          alias: {
            '@': path.resolve(__dirname, 'src')
          }
        },
        
        server: {
          port: 3000,
          open: true,
          proxy: {
            '/api': {
              target: 'http://localhost:4000',
              changeOrigin: true,
              rewrite: (path) => path.replace(/^\/api/, '')
            }
          }
        },
        
        build: {
          outDir: 'dist',
          assetsDir: 'assets',
          rollupOptions: {
            output: {
              manualChunks: {
                vue: ['vue', 'vue-router', 'pinia'],
                utils: ['lodash-es', 'axios']
              }
            }
          },
          terserOptions: {
            compress: {
              drop_console: true
            }
          }
        },
        
        css: {
          preprocessorOptions: {
            scss: {
              additionalData: '@import "@/styles/variables.scss";'
            }
          }
        }
      });
      \`\`\`
      
      ## 四、Babel
      
      ### Q8: Babel的作用和原理？
      
      **作用**：
      - 转换ES6+语法为ES5
      - 转换JSX
      - 添加Polyfill
      
      **原理**：
      \`\`\`javascript
      源代码 -> 解析（Parse）-> AST -> 
      转换（Transform）-> 新AST -> 
      生成（Generate）-> 目标代码
      \`\`\`
      
      **配置示例**：
      \`\`\`javascript
      // .babelrc
      {
        "presets": [
          [
            "@babel/preset-env",
            {
              "targets": "> 0.25%, not dead",
              "useBuiltIns": "usage",
              "corejs": 3
            }
          ],
          "@babel/preset-react"
        ],
        "plugins": [
          "@babel/plugin-proposal-class-properties",
          [
            "@babel/plugin-transform-runtime",
            {
              "corejs": 3
            }
          ]
        ]
      }
      \`\`\`
      
      ## 五、模块化
      
      ### Q9: ES Module和CommonJS的区别？
      
      **ES Module**：
      \`\`\`javascript
      // 静态导入（编译时）
      import { add } from './utils.js';
      
      // 动态导入（运行时）
      import('./utils.js').then(module => {
        module.add(1, 2);
      });
      
      // 导出
      export const add = (a, b) => a + b;
      export default function() {}
      \`\`\`
      
      **CommonJS**：
      \`\`\`javascript
      // 动态导入（运行时）
      const { add } = require('./utils.js');
      
      // 条件导入
      if (condition) {
        const utils = require('./utils.js');
      }
      
      // 导出
      module.exports = {
        add: (a, b) => a + b
      };
      \`\`\`
      
      **主要区别**：
      
      | 特性 | ES Module | CommonJS |
      |------|-----------|----------|
      | 加载时机 | 编译时（静态） | 运行时（动态） |
      | 导出 | export | module.exports |
      | 导入 | import | require |
      | 值引用 | 引用（动态绑定） | 拷贝（值传递） |
      | Tree Shaking | 支持 | 不支持 |
      | 循环依赖 | 支持 | 部分支持 |
      
      ### Q10: 如何实现模块化？
      
      **1. IIFE（立即执行函数）**
      \`\`\`javascript
      const module = (function() {
        let privateVar = 'private';
        
        return {
          publicMethod() {
            return privateVar;
          }
        };
      })();
      \`\`\`
      
      **2. AMD（异步模块定义）**
      \`\`\`javascript
      define(['jquery'], function($) {
        return {
          init() {
            $('body').html('Hello');
          }
        };
      });
      \`\`\`
      
      **3. UMD（通用模块定义）**
      \`\`\`javascript
      (function(root, factory) {
        if (typeof define === 'function' && define.amd) {
          define(['jquery'], factory);
        } else if (typeof module === 'object' && module.exports) {
          module.exports = factory(require('jquery'));
        } else {
          root.myModule = factory(root.jQuery);
        }
      }(this, function($) {
        return {
          init() {}
        };
      }));
      \`\`\`
      
      ## 总结
      
      前端工程化面试重点：
      - Webpack核心概念要清楚
      - 构建优化要有实践经验
      - 理解Vite的优势和原理
      - 掌握Babel配置和使用
      - 了解模块化发展历史
      
      建议：
      - 手写一个简单的Webpack配置
      - 对比Webpack和Vite的差异
      - 理解Tree Shaking原理
      - 实践代码分割和懒加载
      `
      },
      {
        title: 'TypeScript实战：从入门到项目应用',
        slug: 'typescript-practical-guide',
        excerpt: 'TypeScript在实际项目中的应用技巧，包括类型系统、泛型、工具类型等核心概念。',
        category: '后端技术',
        tags: ['TypeScript', '类型系统', '实战'],
        status: 'published',
        isFeatured: false,
        views: 5670,
        likes: 445,
        publishedAt: new Date('2025-08-17'),
        content: `
      TypeScript已经成为大型项目的标配，分享实际应用中的经验。
      
      ## 一、基础类型
      
      ### 基本类型定义
      
      \`\`\`typescript
      // 基本类型
      let str: string = 'hello';
      let num: number = 123;
      let bool: boolean = true;
      let arr: number[] = [1, 2, 3];
      let arr2: Array<number> = [1, 2, 3];
      
      // 元组
      let tuple: [string, number] = ['age', 18];
      
      // 枚举
      enum Color {
        Red = 1,
        Green = 2,
        Blue = 3
      }
      let color: Color = Color.Red;
      
      // any和unknown
      let any: any = 123; // 任意类型
      let unknown: unknown = 123; // 未知类型（更安全）
      
      // void和never
      function log(): void {
        console.log('log');
      }
      
      function error(): never {
        throw new Error('error');
      }
      \`\`\`
      
      ### 接口和类型别名
      
      \`\`\`typescript
      // 接口
      interface User {
        id: number;
        name: string;
        age?: number; // 可选属性
        readonly email: string; // 只读属性
      }
      
      // 类型别名
      type Point = {
        x: number;
        y: number;
      };
      
      // 联合类型
      type ID = string | number;
      
      // 交叉类型
      type Person = {
        name: string;
      };
      
      type Employee = {
        employeeId: number;
      };
      
      type Staff = Person & Employee;
      
      const staff: Staff = {
        name: 'Tom',
        employeeId: 123
      };
      \`\`\`
      
      ## 二、高级类型
      
      ### 泛型
      
      \`\`\`typescript
      // 基础泛型
      function identity<T>(arg: T): T {
        return arg;
      }
      
      identity<number>(123);
      identity('hello'); // 类型推断
      
      // 泛型接口
      interface GenericBox<T> {
        value: T;
        getValue: () => T;
      }
      
      const numberBox: GenericBox<number> = {
        value: 123,
        getValue() {
          return this.value;
        }
      };
      
      // 泛型约束
      interface HasLength {
        length: number;
      }
      
      function logLength<T extends HasLength>(arg: T): T {
        console.log(arg.length);
        return arg;
      }
      
      logLength('hello'); // OK
      logLength([1, 2, 3]); // OK
      // logLength(123); // Error
      \`\`\`
      
      ### 工具类型
      
      \`\`\`typescript
      interface User {
        id: number;
        name: string;
        email: string;
        age: number;
      }
      
      // Partial - 所有属性变为可选
      type PartialUser = Partial<User>;
      // { id?: number; name?: string; email?: string; age?: number; }
      
      // Required - 所有属性变为必选
      type RequiredUser = Required<PartialUser>;
      
      // Readonly - 所有属性变为只读
      type ReadonlyUser = Readonly<User>;
      
      // Pick - 选择部分属性
      type UserPreview = Pick<User, 'id' | 'name'>;
      // { id: number; name: string; }
      
      // Omit - 排除部分属性
      type UserWithoutEmail = Omit<User, 'email'>;
      // { id: number; name: string; age: number; }
      
      // Record - 构造对象类型
      type PageInfo = Record<'home' | 'about' | 'contact', { title: string }>;
      // {
      //   home: { title: string };
      //   about: { title: string };
      //   contact: { title: string };
      // }
      
      // Exclude - 从联合类型中排除
      type T1 = Exclude<'a' | 'b' | 'c', 'a'>;
      // 'b' | 'c'
      
      // Extract - 从联合类型中提取
      type T2 = Extract<'a' | 'b' | 'c', 'a' | 'f'>;
      // 'a'
      
      // NonNullable - 排除null和undefined
      type T3 = NonNullable<string | number | null | undefined>;
      // string | number
      
      // ReturnType - 获取函数返回值类型
      function getUser() {
        return { id: 1, name: 'Tom' };
      }
      
      type User = ReturnType<typeof getUser>;
      // { id: number; name: string; }
      \`\`\`
      
      ## 三、实战应用
      
      ### API响应类型定义
      
      \`\`\`typescript
      // 通用响应类型
      interface ApiResponse<T> {
        code: number;
        message: string;
        data: T;
      }
      
      // 用户数据类型
      interface UserData {
        id: number;
        name: string;
        email: string;
      }
      
      // 列表响应类型
      interface ListResponse<T> {
        list: T[];
        total: number;
        page: number;
        pageSize: number;
      }
      
      // 使用示例
      async function fetchUser(id: number): Promise<ApiResponse<UserData>> {
        const response = await fetch(\`/api/users/\${id}\`);
        return response.json();
      }
      
      async function fetchUserList(): Promise<ApiResponse<ListResponse<UserData>>> {
        const response = await fetch('/api/users');
        return response.json();
      }
      
      // 使用
      const userResponse = await fetchUser(1);
      console.log(userResponse.data.name); // 类型安全
      \`\`\`
      
      ### React组件类型
      
      \`\`\`typescript
      import React, { FC, ReactNode, useState } from 'react';
      
      // Props类型定义
      interface ButtonProps {
        type?: 'primary' | 'default' | 'danger';
        size?: 'small' | 'medium' | 'large';
        disabled?: boolean;
        onClick?: () => void;
        children: ReactNode;
      }
      
      // 函数组件
      const Button: FC<ButtonProps> = ({
        type = 'default',
        size = 'medium',
        disabled = false,
        onClick,
        children
      }) => {
        return (
          <button
            className={\`btn btn-\${type} btn-\${size}\`}
            disabled={disabled}
            onClick={onClick}
          >
            {children}
          </button>
        );
      };
      
      // 带泛型的组件
      interface SelectProps<T> {
        options: T[];
        value: T;
        onChange: (value: T) => void;
        getLabel: (option: T) => string;
      }
      
      function Select<T>({ options, value, onChange, getLabel }: SelectProps<T>) {
        return (
          <select
            value={getLabel(value)}
            onChange={(e) => {
              const option = options.find(
                (opt) => getLabel(opt) === e.target.value
              );
              if (option) onChange(option);
            }}
          >
            {options.map((option, index) => (
              <option key={index} value={getLabel(option)}>
                {getLabel(option)}
              </option>
            ))}
          </select>
        );
      }
      
      // 使用
      interface User {
        id: number;
        name: string;
      }
      
      const users: User[] = [
        { id: 1, name: 'Tom' },
        { id: 2, name: 'Jerry' }
      ];
      
      <Select
        options={users}
        value={users[0]}
        onChange={(user) => console.log(user)}
        getLabel={(user) => user.name}
      />
      \`\`\`
      
      ### Vue3组合式API类型
      
      \`\`\`typescript
      import { ref, computed, Ref, ComputedRef } from 'vue';
      
      // 定义组件Props
      interface Props {
        title: string;
        count?: number;
      }
      
      // 使用defineComponent
      import { defineComponent, PropType } from 'vue';
      
      export default defineComponent({
        props: {
          title: {
            type: String,
            required: true
          },
          users: {
            type: Array as PropType<User[]>,
            default: () => []
          }
        },
        setup(props) {
          // props是类型安全的
          console.log(props.title);
        }
      });
      
      // 组合式函数类型
      interface UseCounterReturn {
        count: Ref<number>;
        doubleCount: ComputedRef<number>;
        increment: () => void;
        decrement: () => void;
      }
      
      function useCounter(initialValue = 0): UseCounterReturn {
        const count = ref(initialValue);
        
        const doubleCount = computed(() => count.value * 2);
        
        const increment = () => {
          count.value++;
        };
        
        const decrement = () => {
          count.value--;
        };
        
        return {
          count,
          doubleCount,
          increment,
          decrement
        };
      }
      
      // 使用
      const { count, doubleCount, increment } = useCounter(10);
      \`\`\`
      
      ### Express路由类型
      
      \`\`\`typescript
      import express, { Request, Response, NextFunction } from 'express';
      
      // 扩展Request类型
      interface AuthRequest extends Request {
        user?: {
          id: number;
          email: string;
        };
      }
      
      // 中间件类型
      const authMiddleware = (
        req: AuthRequest,
        res: Response,
        next: NextFunction
      ) => {
        const token = req.headers.authorization;
        
        if (!token) {
          return res.status(401).json({ error: 'Unauthorized' });
        }
        
        // 验证token，设置user
        req.user = { id: 1, email: 'user@example.com' };
        next();
      };
      
      // 路由处理器类型
      interface CreateUserBody {
        name: string;
        email: string;
        password: string;
      }
      
      app.post('/api/users', (
        req: Request<{}, {}, CreateUserBody>,
        res: Response
      ) => {
        const { name, email, password } = req.body; // 类型安全
        // 创建用户逻辑
        res.json({ success: true });
      });
      
      // 带路径参数的路由
      interface UserParams {
        id: string;
      }
      
      app.get('/api/users/:id', (
        req: Request<UserParams>,
        res: Response
      ) => {
        const userId = parseInt(req.params.id); // 类型安全
        // 获取用户逻辑
        res.json({ id: userId, name: 'Tom' });
      });
      \`\`\`
      
      ## 四、常见问题
      
      ### 类型断言
      
      \`\`\`typescript
      // as语法
      const input = document.querySelector('#input') as HTMLInputElement;
      input.value = 'hello';
      
      // 尖括号语法（JSX中不能用）
      const input = <HTMLInputElement>document.querySelector('#input');
      
      // 双重断言（不推荐）
      const num = ('123' as any) as number;
      
      // 非空断言
      function processValue(value?: string) {
        // 确定value不为空时使用
        console.log(value!.toUpperCase());
      }
      \`\`\`
      
      ### 类型守卫
      
      \`\`\`typescript
      // typeof类型守卫
      function print(value: string | number) {
        if (typeof value === 'string') {
          console.log(value.toUpperCase()); // string类型
        } else {
          console.log(value.toFixed(2)); // number类型
        }
      }
      
      // instanceof类型守卫
      class Dog {
        bark() {
          console.log('Woof!');
        }
      }
      
      class Cat {
        meow() {
          console.log('Meow!');
        }
      }
      
      function makeSound(animal: Dog | Cat) {
        if (animal instanceof Dog) {
          animal.bark();
        } else {
          animal.meow();
        }
      }
      
      // 自定义类型守卫
      interface Fish {
        swim: () => void;
      }
      
      interface Bird {
        fly: () => void;
      }
      
      function isFish(animal: Fish | Bird): animal is Fish {
        return (animal as Fish).swim !== undefined;
      }
      
      function move(animal: Fish | Bird) {
        if (isFish(animal)) {
          animal.swim();
        } else {
          animal.fly();
        }
      }
      \`\`\`
      
      ### 装饰器
      
      \`\`\`typescript
      // 类装饰器
      function Component(target: Function) {
        target.prototype.isComponent = true;
      }
      
      @Component
      class MyComponent {}
      
      // 方法装饰器
      function log(
        target: any,
        propertyKey: string,
        descriptor: PropertyDescriptor
      ) {
        const original = descriptor.value;
        
        descriptor.value = function(...args: any[]) {
          console.log(\`Calling \${propertyKey} with\`, args);
          const result = original.apply(this, args);
          console.log(\`Result:\`, result);
          return result;
        };
        
        return descriptor;
      }
      
      class Calculator {
        @log
        add(a: number, b: number) {
          return a + b;
        }
      }
      
      // 属性装饰器
      function MinLength(length: number) {
        return function(target: any, propertyKey: string) {
          let value: string;
          
          const getter = () => value;
          const setter = (newValue: string) => {
            if (newValue.length < length) {
              throw new Error(\`\${propertyKey} must be at least \${length} characters\`);
            }
            value = newValue;
          };
          
          Object.defineProperty(target, propertyKey, {
            get: getter,
            set: setter
          });
        };
      }
      
      class User {
        @MinLength(3)
        name: string;
      }
      \`\`\`
      
      ## 总结
      
      TypeScript实战建议：
      - 充分利用类型系统的优势
      - 合理使用泛型提高代码复用性
      - 善用工具类型简化类型定义
      - 为API响应和组件Props定义准确类型
      - 使用类型守卫处理联合类型
      - 配置严格模式获得更好的类型检查
      
      TypeScript不是负担，而是帮助我们写出更健壮代码的工具。
      `
      },
      {
        title: 'Node.js性能优化：从10req/s到1000req/s',
        slug: 'nodejs-performance-optimization',
        excerpt: '记录一次完整的Node.js性能优化过程，包括数据库优化、缓存策略、集群模式等。',
        category: '后端技术',
        tags: ['Node.js', '性能优化', '后端'],
        status: 'published',
        isFeatured: true,
        views: 6890,
        likes: 578,
        publishedAt: new Date('2025-08-15'),
        content: `
      线上Node.js服务性能不理想，并发量上不去。经过一系列优化，性能提升了100倍。
      
      ## 一、性能分析
      
      ### 使用clinic诊断
      
      \`\`\`bash
      # 安装clinic
      npm install -g clinic
      
      # 分析性能
      clinic doctor -- node app.js
      clinic flame -- node app.js
      clinic bubbleprof -- node app.js
      
      # 生成报告并自动打开浏览器
      \`\`\`
      
      ### 使用console.time测量
      
      \`\`\`javascript
      console.time('database query');
      const users = await User.findAll();
      console.timeEnd('database query');
      // database query: 523.456ms
      
      // 或使用performance API
      const { performance } = require('perf_hooks');
      
      const start = performance.now();
      // 执行操作
      const end = performance.now();
      console.log(\`耗时: \${end - start}ms\`);
      \`\`\`
      
      ## 二、数据库优化
      
      ### 添加索引
      
      \`\`\`javascript
      // 发现慢查询
      SELECT * FROM users WHERE email = 'user@example.com'; 
      // 扫描: 100000行，耗时: 523ms
      
      // 添加索引
      CREATE INDEX idx_users_email ON users(email);
      
      // 优化后
      SELECT * FROM users WHERE email = 'user@example.com';
      // 扫描: 1行，耗时: 2ms
      \`\`\`
      
      ### 使用连接池
      
      \`\`\`javascript
      // 不好：每次创建新连接
      const mysql = require('mysql');
      
      app.get('/users', async (req, res) => {
        const connection = mysql.createConnection(config);
        connection.connect();
        // 查询...
        connection.end();
      });
      
      // 好：使用连接池
      const pool = mysql.createPool({
        host: 'localhost',
        user: 'root',
        password: 'password',
        database: 'mydb',
        connectionLimit: 10, // 连接池大小
        queueLimit: 0
      });
      
      app.get('/users', (req, res) => {
        pool.query('SELECT * FROM users', (error, results) => {
          res.json(results);
        });
      });
      \`\`\`
      
      ### 避免N+1查询
      
      \`\`\`javascript
      // 不好：N+1查询
      const users = await User.findAll();
      for (const user of users) {
        user.posts = await Post.findAll({ where: { userId: user.id } });
      }
      // 查询次数: 1 + N
      
      // 好：使用JOIN或eager loading
      const users = await User.findAll({
        include: [{ model: Post }]
      });
      // 查询次数: 1
      
      // 或使用DataLoader批量加载
      const DataLoader = require('dataloader');
      
      const postLoader = new DataLoader(async (userIds) => {
        const posts = await Post.findAll({
          where: { userId: userIds }
        });
        
        // 按userId分组
        const grouped = userIds.map(id =>
          posts.filter(post => post.userId === id)
        );
        
        return grouped;
      });
      
      // 使用
      const posts = await postLoader.load(userId);
      \`\`\`
      
      ## 三、缓存策略
      
      ### Redis缓存
      
      \`\`\`javascript
      const redis = require('redis');
      const client = redis.createClient();
      
      // 缓存函数
      async function cacheWrapper(key, ttl, fn) {
        // 先查缓存
        const cached = await client.get(key);
        if (cached) {
          return JSON.parse(cached);
        }
        
        // 缓存不存在，执行函数
        const result = await fn();
        
        // 存入缓存
        await client.setex(key, ttl, JSON.stringify(result));
        
        return result;
      }
      
      // 使用
      app.get('/api/users/:id', async (req, res) => {
        const { id } = req.params;
        
        const user = await cacheWrapper(
          \`user:\${id}\`,
          3600, // 1小时
          async () => {
            return await User.findByPk(id);
          }
        );
        
        res.json(user);
      });
      \`\`\`
      
      ### 内存缓存
      
      \`\`\`javascript
      const NodeCache = require('node-cache');
      const cache = new NodeCache({ stdTTL: 600 });
      
      app.get('/api/config', (req, res) => {
        let config = cache.get('config');
        
        if (!config) {
          // 从数据库或文件读取配置
          config = loadConfig();
          cache.set('config', config);
        }
        
        res.json(config);
      });
      \`\`\`
      
      ## 四、异步优化
      
      ### 使用Promise.all并发
      
      \`\`\`javascript
      // 不好：串行执行
      async function getUserData(userId) {
        const user = await User.findByPk(userId);
        const posts = await Post.findAll({ where: { userId } });
        const comments = await Comment.findAll({ where: { userId } });
        
        return { user, posts, comments };
      }
      // 总耗时 = 50ms + 100ms + 80ms = 230ms
      
      // 好：并发执行
      async function getUserData(userId) {
        const [user, posts, comments] = await Promise.all([
          User.findByPk(userId),
          Post.findAll({ where: { userId } }),
          Comment.findAll({ where: { userId } })
        ]);
        
        return { user, posts, comments };
      }
      // 总耗时 = max(50ms, 100ms, 80ms) = 100ms
      \`\`\`
      
      ### 流式处理
      
      \`\`\`javascript
      const fs = require('fs');
      
      // 不好：一次性读取大文件
      app.get('/download', (req, res) => {
        const content = fs.readFileSync('large-file.zip');
        res.send(content);
      });
      // 内存占用高，响应慢
      
      // 好：使用流
      app.get('/download', (req, res) => {
        const stream = fs.createReadStream('large-file.zip');
        stream.pipe(res);
      });
      // 内存占用低，响应快
      \`\`\`
      
      ## 五、集群模式
      
      ### 使用cluster模块
      
      \`\`\`javascript
      const cluster = require('cluster');
      const os = require('os');
      
      if (cluster.isMaster) {
        const numCPUs = os.cpus().length;
        
        console.log(\`Master进程 \${process.pid} 正在运行\`);
        
        // 衍生工作进程
        for (let i = 0; i < numCPUs; i++) {
          cluster.fork();
        }
        
        cluster.on('exit', (worker, code, signal) => {
          console.log(\`工作进程 \${worker.process.pid} 已退出\`);
          // 重启工作进程
          cluster.fork();
        });
      } else {
        // 工作进程可以共享TCP连接
        const app = require('./app');
        
        app.listen(3000, () => {
          console.log(\`工作进程 \${process.pid} 已启动\`);
        });
      }
      \`\`\`
      
      ### 使用PM2
      
      \`\`\`javascript
      // ecosystem.config.js
      module.exports = {
        apps: [{
          name: 'api',
          script: './app.js',
          instances: 'max', // 使用所有CPU核心
          exec_mode: 'cluster',
          env: {
            NODE_ENV: 'production'
          },
          error_file: './logs/err.log',
          out_file: './logs/out.log',
          log_date_format: 'YYYY-MM-DD HH:mm:ss',
          merge_logs: true
        }]
      };
      
      // 启动
      // pm2 start ecosystem.config.js
      \`\`\`
      
      ## 六、其他优化
      
      ### 压缩响应
      
      \`\`\`javascript
      const compression = require('compression');
      
      app.use(compression({
        level: 6, // 压缩级别 0-9
        threshold: 1024, // 大于1KB才压缩
        filter: (req, res) => {
          if (req.headers['x-no-compression']) {
            return false;
          }
          return compression.filter(req, res);
        }
      }));
      \`\`\`
      
      ### 使用CDN
      
      \`\`\`javascript
      // 静态资源使用CDN
      app.use('/static', express.static('public', {
        maxAge: '1y', // 设置缓存时间
        etag: true
      }));
      
      // 在HTML中使用CDN URL
      <script src="https://cdn.example.com/static/app.js"></script>
      \`\`\`
      
      ### 限流
      
      \`\`\`javascript
      const rateLimit = require('express-rate-limit');
      
      const limiter = rateLimit({
        windowMs: 15 * 60 * 1000, // 15分钟
        max: 100, // 最多100个请求
        message: '请求过于频繁，请稍后再试'
      });
      
      app.use('/api/', limiter);
      \`\`\`
      
      ### 健康检查
      
      \`\`\`javascript
      app.get('/health', (req, res) => {
        res.json({
          status: 'ok',
          uptime: process.uptime(),
          memory: process.memoryUsage(),
          timestamp: Date.now()
        });
      });
      \`\`\`
      
      ## 七、性能监控
      
      ### 使用prom-client监控
      
      \`\`\`javascript
      const promClient = require('prom-client');
      
      // 创建指标
      const httpRequestDuration = new promClient.Histogram({
        name: 'http_request_duration_seconds',
        help: 'HTTP请求耗时',
        labelNames: ['method', 'route', 'status_code']
      });
      
      // 中间件
      app.use((req, res, next) => {
        const start = Date.now();
        
        res.on('finish', () => {
          const duration = (Date.now() - start) / 1000;
          httpRequestDuration
            .labels(req.method, req.route?.path || req.path, res.statusCode)
            .observe(duration);
        });
        
        next();
      });
      
      // 暴露指标端点
      app.get('/metrics', async (req, res) => {
        res.set('Content-Type', promClient.register.contentType);
        res.send(await promClient.register.metrics());
      });
      \`\`\`
      
      ## 优化效果
      
      **优化前**：
      - 并发: 10 req/s
      - 响应时间: 2000ms
      - CPU占用: 80%
      - 内存占用: 1.5GB
      
      **优化后**：
      - 并发: 1000 req/s（提升100倍）
      - 响应时间: 50ms（减少40倍）
      - CPU占用: 40%（降低50%）
      - 内存占用: 512MB（减少66%）
      
      ## 总结
      
      Node.js性能优化要点：
      - 数据库查询是性能瓶颈，务必优化
      - 合理使用缓存，减少重复计算
      - 充分利用异步和并发能力
      - 使用集群模式利用多核CPU
      - 添加监控，及时发现问题
      
      性能优化是个持续的过程，需要不断测量、分析、优化。
      `
      },
      {
        title: 'Nginx完全指南：从入门到实战与面试必备',
        slug: 'nginx-complete-guide',
        excerpt: 'Nginx知识体系全面整理，包括基础配置、反向代理、负载均衡、性能优化和面试高频题。',
        category: '学习笔记',
        tags: ['Nginx', '运维', '面试'],
        status: 'published',
        isFeatured: true,
        views: 7230,
        likes: 612,
        publishedAt: new Date('2025-08-13'),
        content: `
      整理了Nginx的核心知识点和实际应用经验，包含面试常见问题。
      
      ## 一、Nginx基础
      
      ### 什么是Nginx？
      
      Nginx是一个高性能的HTTP和反向代理服务器，特点：
      - 高并发能力（可处理数万并发连接）
      - 低内存消耗
      - 高可靠性
      - 热部署（不停机更新配置）
      - 模块化设计
      
      **常见用途**：
      - 静态资源服务器
      - 反向代理
      - 负载均衡
      - HTTP缓存
      - SSL/TLS终端
      
      ### 基本配置结构
      
      **nginx.conf结构**：
      
      全局块配置示例：
      - user nginx
      - worker_processes auto（工作进程数）
      - error_log /var/log/nginx/error.log
      - pid /run/nginx.pid
      
      events块配置示例：
      - worker_connections 1024（每个进程的最大连接数）
      - use epoll（使用epoll事件模型）
      
      http块配置示例：
      - include mime.types
      - default_type application/octet-stream
      - log_format设置
      - access_log配置
      - sendfile on
      - keepalive_timeout 65
      - gzip on
      
      server块配置示例：
      - listen 80
      - server_name example.com
      - location / 配置
      
      ### 常用指令说明
      
      **监听端口**：
      - listen 80
      - listen 443 ssl http2
      - listen [::]:80（IPv6）
      
      **服务器名称**：
      - server_name example.com www.example.com
      - server_name *.example.com（通配符）
      - server_name 使用正则表达式
      
      **其他常用指令**：
      - root /var/www/html（根目录）
      - index index.html index.htm（默认文件）
      - access_log /var/log/nginx/access.log（访问日志）
      - error_log /var/log/nginx/error.log warn（错误日志）
      
      ## 二、静态资源服务
      
      ### 基本配置
      
      配置静态网站服务：
      - 设置root目录指向静态文件
      - 配置try_files处理文件查找
      - 为不同类型资源设置缓存策略
      
      **图片资源缓存**：
      - 设置expires 30d
      - 添加Cache-Control: "public, immutable"
      
      **CSS和JS缓存**：
      - 设置expires 7d
      - 添加Cache-Control: "public"
      
      **字体文件处理**：
      - 设置expires 1y
      - 添加Access-Control-Allow-Origin
      
      ### 开启Gzip压缩
      
      Gzip配置要点：
      - gzip on 开启压缩
      - gzip_comp_level 6（压缩级别1-9，推荐6）
      - gzip_min_length 1000（最小压缩文件大小）
      - gzip_types 指定压缩的MIME类型
      - gzip_proxied any（对代理请求也压缩）
      - gzip_vary on（增加Vary响应头）
      - gzip_disable "msie6"（禁用IE6的gzip）
      
      ## 三、反向代理
      
      ### 基本反向代理配置
      
      **核心配置**：
      - proxy_pass指向后端服务地址
      - proxy_set_header Host $host（传递主机头）
      - proxy_set_header X-Real-IP $remote_addr（传递真实IP）
      - proxy_set_header X-Forwarded-For（传递代理链）
      - proxy_set_header X-Forwarded-Proto（传递协议）
      
      **超时设置**：
      - proxy_connect_timeout 60s
      - proxy_send_timeout 60s
      - proxy_read_timeout 60s
      
      **缓冲设置**：
      - proxy_buffering on
      - proxy_buffer_size 4k
      - proxy_buffers 8 4k
      
      ### WebSocket代理
      
      WebSocket代理必需配置：
      - proxy_http_version 1.1（HTTP版本）
      - proxy_set_header Upgrade $http_upgrade（升级协议）
      - proxy_set_header Connection "upgrade"（连接升级）
      - 设置更长的超时时间（如3600s）
      
      ## 四、负载均衡
      
      ### 负载均衡策略
      
      **1. 轮询（默认）**：
      - 按顺序依次分配请求到各服务器
      
      **2. 加权轮询**：
      - 使用weight参数设置权重
      - 权重越高，分配的请求越多
      
      **3. IP哈希**：
      - 使用ip_hash指令
      - 同一IP总是访问同一台服务器
      - 适合需要会话保持的场景
      
      **4. 最少连接**：
      - 使用least_conn指令
      - 将请求分配给连接数最少的服务器
      
      ### 健康检查
      
      服务器状态控制：
      - max_fails=3（最大失败次数）
      - fail_timeout=30s（失败超时时间）
      - backup（标记为备用服务器）
      - down（标记服务器下线）
      
      ## 五、HTTPS配置
      
      ### SSL证书配置要点
      
      **基本配置**：
      - HTTP到HTTPS重定向
      - 监听443端口，启用ssl和http2
      - 配置证书文件路径
      - 设置SSL协议版本（TLSv1.2、TLSv1.3）
      - 配置加密套件
      - 启用SSL会话缓存
      
      **安全增强**：
      - 添加HSTS头部
      - ssl_session_cache共享缓存
      - ssl_session_timeout设置超时
      
      ### Let's Encrypt免费证书
      
      使用certbot申请证书：
      1. 安装certbot和nginx插件
      2. 运行certbot --nginx命令
      3. 配置自动续期
      4. 添加cron定时任务
      
      ## 六、常见配置场景
      
      ### SPA单页应用配置
      
      **关键配置**：
      - 所有路由返回index.html
      - 使用try_files $uri $uri/ /index.html
      - API请求代理到后端
      - 静态资源设置长缓存
      
      ### 跨域CORS配置
      
      **CORS响应头设置**：
      - Access-Control-Allow-Origin
      - Access-Control-Allow-Methods
      - Access-Control-Allow-Headers
      - Access-Control-Allow-Credentials
      - Access-Control-Max-Age
      
      **处理OPTIONS预检请求**：
      - 检测request_method
      - OPTIONS请求直接返回204
      
      ### 防盗链配置
      
      使用valid_referers指令：
      - 检查HTTP Referer头
      - 允许指定域名访问
      - invalid_referer时返回403或默认图片
      
      ### 访问限制
      
      **限流配置**：
      - limit_req_zone定义限流区域
      - limit_req应用限流规则
      - limit_conn_zone限制连接数
      - limit_conn应用连接限制
      
      **IP黑白名单**：
      - allow指令允许访问
      - deny指令拒绝访问
      - 支持IP段配置
      
      ## 七、性能优化
      
      ### 工作进程优化
      
      **进程配置**：
      - worker_processes auto（自动设为CPU核心数）
      - worker_cpu_affinity auto（绑定CPU核心）
      - worker_priority设置优先级
      - worker_rlimit_nofile设置文件描述符
      - worker_connections设置最大连接数
      
      **事件模型**：
      - use epoll（Linux推荐）
      - multi_accept on（同时接受多个连接）
      
      ### 连接优化
      
      **关键配置**：
      - sendfile on（零拷贝传输）
      - tcp_nopush on（优化数据包）
      - tcp_nodelay on（禁用Nagle算法）
      - keepalive_timeout 65（长连接超时）
      - keepalive_requests 100（长连接最大请求数）
      
      **缓冲区配置**：
      - client_max_body_size限制请求体大小
      - client_header_buffer_size头部缓冲区
      
      ### 缓存配置
      
      **代理缓存**：
      - proxy_cache_path定义缓存路径
      - proxy_cache使用缓存
      - proxy_cache_valid设置缓存时间
      - proxy_cache_key设置缓存键
      - proxy_cache_bypass缓存绕过条件
      
      **文件缓存**：
      - open_file_cache缓存打开的文件
      - open_file_cache_valid验证时间
      - open_file_cache_min_uses最小使用次数
      
      ## 八、监控与日志
      
      ### 自定义日志格式
      
      **main格式包含**：
      - 客户端IP
      - 访问时间
      - 请求信息
      - 状态码
      - 字节数
      - Referer和User-Agent
      - 请求时间
      - 上游响应时间
      
      **JSON格式日志**：
      - 使用escape=json
      - 方便日志分析工具处理
      
      ### 状态监控
      
      启用stub_status模块：
      - 访问/nginx_status端点
      - 查看活跃连接数
      - 查看accepts、handled、requests统计
      - 查看Reading、Writing、Waiting状态
      
      ## 九、常见问题排查
      
      ### 命令行工具
      
      **常用命令**：
      - nginx -v（查看版本）
      - nginx -V（查看编译参数）
      - nginx -t（测试配置文件）
      - nginx -T（测试并输出配置）
      - nginx -s reload（重载配置）
      - nginx -s stop（快速停止）
      - nginx -s quit（优雅停止）
      
      ### 常见错误处理
      
      **502 Bad Gateway**：
      - 检查后端服务是否启动
      - 检查防火墙设置
      - 增加proxy_timeout时间
      
      **413 Request Entity Too Large**：
      - 增加client_max_body_size
      
      **504 Gateway Timeout**：
      - 增加proxy_read_timeout
      
      ## 十、面试高频问题
      
      ### Q1: Nginx的工作原理？
      
      **Master-Worker模型**：
      - Master进程：管理Worker，读取配置，绑定端口
      - Worker进程：处理实际请求，数量设为CPU核心数
      - 事件驱动：使用epoll实现高并发
      
      **工作流程**：
      1. Master启动并读取配置
      2. Master创建Worker进程
      3. 客户端连接，Worker竞争accept
      4. Worker使用事件循环处理（非阻塞）
      5. 响应返回客户端
      
      ### Q2: Nginx vs Apache？
      
      **主要区别**：
      
      架构方面：
      - Nginx：事件驱动，异步非阻塞
      - Apache：进程/线程模型
      
      性能方面：
      - Nginx：高并发（数万），低内存
      - Apache：中等并发（数千），内存较高
      
      静态文件：
      - Nginx：处理速度快
      - Apache：相对较慢
      
      动态内容：
      - Nginx：需要FastCGI
      - Apache：原生支持
      
      配置灵活性：
      - Nginx：简单清晰
      - Apache：复杂灵活，支持.htaccess
      
      ### Q3: 正向代理 vs 反向代理？
      
      **正向代理**：
      - 客户端的代理
      - 隐藏客户端身份
      - 场景：访问限制资源
      
      **反向代理**：
      - 服务器的代理
      - 隐藏服务器信息
      - 场景：负载均衡、SSL终端、缓存
      
      ### Q4: 如何实现灰度发布？
      
      基于请求ID或其他标识进行流量分配：
      - 定义多个upstream（v1、v2）
      - 根据条件判断转发到不同版本
      - 逐步增加新版本流量比例
      
      ### Q5: 如何优化Nginx性能？
      
      **系统层面**：
      - 增加文件描述符限制
      - 调整内核参数
      
      **配置层面**：
      - 设置合理worker数量
      - 开启sendfile、tcp_nopush
      - 使用upstream长连接
      - 启用缓存机制
      
      **网络层面**：
      - 启用gzip压缩
      - 使用HTTP/2协议
      - 配置CDN
      
      **业务层面**：
      - 静态资源分离
      - 图片优化
      - 代码压缩
      
      ### Q6: location匹配规则优先级？
      
      **优先级从高到低**：
      1. 精确匹配：= /exact
      2. 前缀匹配（优先）：^~ /prefix
      3. 正则匹配（区分大小写）：~ \\.php$
      4. 正则匹配（不区分大小写）：~* \\.(jpg|png)$
      5. 普通前缀匹配：/general
      6. 通用匹配：/
      
      ### Q7: 如何实现会话保持？
      
      **方法1：ip_hash**
      - 基于客户端IP哈希
      - 同一IP访问同一服务器
      
      **方法2：sticky模块**
      - 基于cookie
      - 需要第三方模块
      
      **方法3：应用层处理**
      - 使用Redis共享session
      - 推荐方案，更灵活
      
      ### Q8: 如何处理高并发？
      
      **优化策略**：
      - 调整worker_processes和worker_connections
      - 使用epoll事件模型
      - 启用长连接keepalive
      - 配置合理的超时时间
      - 使用缓存减少后端压力
      - 启用gzip减少传输量
      - 静态资源使用CDN
      - 数据库使用连接池
      - 应用层使用异步处理
      
      ### Q9: 如何保证高可用？
      
      **方案**：
      - Keepalived + Nginx主备
      - 多个Nginx + DNS轮询
      - 使用负载均衡器（如LVS）
      - 健康检查自动剔除故障节点
      - 监控告警及时发现问题
      
      ### Q10: Nginx如何实现动静分离？
      
      **配置方式**：
      - 静态资源location直接返回文件
      - 动态请求location代理到后端
      - 静态资源可以配置更长缓存时间
      - 静态资源可以部署到CDN
      
      ## 总结
      
      **Nginx学习路线**：
      1. 掌握基本配置语法
      2. 理解工作原理和进程模型
      3. 熟练配置常见场景
      4. 学习性能优化方法
      5. 了解监控和排错
      6. 深入研究源码
      
      **面试准备重点**：
      - Nginx工作原理必须清楚
      - 反向代理和负载均衡是核心
      - 性能优化要有实践经验
      - 与Apache对比要了解
      - 准备1-2个实际项目案例
      
      **实践建议**：
      - 本地搭建Nginx环境
      - 配置不同应用场景
      - 使用压测工具测试性能
      - 阅读官方文档
      - 参与实际项目部署
      
      Nginx是Web开发和运维必备技能，值得深入学习和实践。
      `
      }
  ];

  // 批量插入文章
  for (const articleData of articlesData) {
    // 清理 content：去除每行前导空格，避免 marked 将其识别为代码块
    const cleanContent = articleData.content
      .split('\n')
      .map(line => line.trimStart())
      .join('\n')
      .trim();
    
    // 将 Markdown 转换为 HTML
    const contentHtml = await marked.parse(cleanContent);
    
    const article = articleRepository.create({
      ...articleData,
      authorId: admin.id,
      content: cleanContent, // 使用清理后的 content
      contentHtml: contentHtml,
    });

    await articleRepository.save(article);
    console.log(`✅ 已导入文章: ${article.title}`);
  }

  console.log(`\n✅ 成功导入 ${articlesData.length} 篇文章`);
}
